Add support for regex in !search via -regex/-re
This commit is contained in:
parent
c1cb5a4ed7
commit
a0edd962f3
3 changed files with 53 additions and 10 deletions
backend
6
backend/package-lock.json
generated
6
backend/package-lock.json
generated
|
@ -598,6 +598,12 @@
|
||||||
"integrity": "sha512-ewFXqrQHlFsgc09MK5jP5iR7vumV/BYayNC6PgJO2LPe8vrnNFyjQjSppfEngITi0qvfKtzFvgKymGheFM9UOA==",
|
"integrity": "sha512-ewFXqrQHlFsgc09MK5jP5iR7vumV/BYayNC6PgJO2LPe8vrnNFyjQjSppfEngITi0qvfKtzFvgKymGheFM9UOA==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"@types/safe-regex": {
|
||||||
|
"version": "1.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/safe-regex/-/safe-regex-1.1.2.tgz",
|
||||||
|
"integrity": "sha512-wuS9LVpgIiTYaGKd+s6Dj0kRXBkttaXjVxzaXmviCACi8RO+INPayND+VNjAcall/l1Jkyhh9lyPfKW/aP/Yug==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"@types/serve-static": {
|
"@types/serve-static": {
|
||||||
"version": "1.13.3",
|
"version": "1.13.3",
|
||||||
"resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.3.tgz",
|
"resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.3.tgz",
|
||||||
|
|
|
@ -67,6 +67,7 @@
|
||||||
"@types/passport": "^1.0.0",
|
"@types/passport": "^1.0.0",
|
||||||
"@types/passport-oauth2": "^1.4.8",
|
"@types/passport-oauth2": "^1.4.8",
|
||||||
"@types/passport-strategy": "^0.2.35",
|
"@types/passport-strategy": "^0.2.35",
|
||||||
|
"@types/safe-regex": "^1.1.2",
|
||||||
"@types/tmp": "0.0.33",
|
"@types/tmp": "0.0.33",
|
||||||
"ava": "^2.4.0",
|
"ava": "^2.4.0",
|
||||||
"rimraf": "^2.6.2",
|
"rimraf": "^2.6.2",
|
||||||
|
|
|
@ -58,6 +58,8 @@ import LCL from "last-commit-log";
|
||||||
import * as t from "io-ts";
|
import * as t from "io-ts";
|
||||||
import { ICommandDefinition } from "knub-command-manager";
|
import { ICommandDefinition } from "knub-command-manager";
|
||||||
import path from "path";
|
import path from "path";
|
||||||
|
import escapeStringRegexp from "escape-string-regexp";
|
||||||
|
import safeRegex from "safe-regex";
|
||||||
|
|
||||||
const ConfigSchema = t.type({
|
const ConfigSchema = t.type({
|
||||||
can_roles: t.boolean,
|
can_roles: t.boolean,
|
||||||
|
@ -97,8 +99,11 @@ type MemberSearchParams = {
|
||||||
bot?: boolean;
|
bot?: boolean;
|
||||||
sort?: string;
|
sort?: string;
|
||||||
"case-sensitive"?: boolean;
|
"case-sensitive"?: boolean;
|
||||||
|
regex?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class SearchError extends Error {}
|
||||||
|
|
||||||
export class UtilityPlugin extends ZeppelinPlugin<TConfigSchema> {
|
export class UtilityPlugin extends ZeppelinPlugin<TConfigSchema> {
|
||||||
public static pluginName = "utility";
|
public static pluginName = "utility";
|
||||||
public static configSchema = ConfigSchema;
|
public static configSchema = ConfigSchema;
|
||||||
|
@ -334,17 +339,22 @@ export class UtilityPlugin extends ZeppelinPlugin<TConfigSchema> {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (args.query) {
|
if (args.query) {
|
||||||
const query = args["case-sensitive"] ? args.query.trimStart() : args.query.toLowerCase().trimStart();
|
let queryRegex: RegExp;
|
||||||
|
if (args.regex) {
|
||||||
|
queryRegex = new RegExp(args.query.trimStart(), args["case-sensitive"] ? "i" : "");
|
||||||
|
} else {
|
||||||
|
queryRegex = new RegExp(escapeStringRegexp(args.query.trimStart()), args["case-sensitive"] ? "i" : "");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!safeRegex(queryRegex)) {
|
||||||
|
throw new SearchError("Unsafe/too complex regex (star depth is limited to 1)");
|
||||||
|
}
|
||||||
|
|
||||||
matchingMembers = matchingMembers.filter(member => {
|
matchingMembers = matchingMembers.filter(member => {
|
||||||
const nick = args["case-sensitive"] ? member.nick : member.nick && member.nick.toLowerCase();
|
if (member.nick && member.nick.match(queryRegex)) return true;
|
||||||
|
|
||||||
const fullUsername = args["case-sensitive"]
|
const fullUsername = `${member.user.username}#${member.user.discriminator}`;
|
||||||
? `${member.user.username}#${member.user.discriminator}`
|
if (fullUsername.match(queryRegex)) return true;
|
||||||
: `${member.user.username}#${member.user.discriminator}`.toLowerCase();
|
|
||||||
|
|
||||||
if (nick && nick.indexOf(query) !== -1) return true;
|
|
||||||
if (fullUsername.indexOf(query) !== -1) return true;
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
|
@ -423,6 +433,11 @@ export class UtilityPlugin extends ZeppelinPlugin<TConfigSchema> {
|
||||||
name: "ids",
|
name: "ids",
|
||||||
isSwitch: true,
|
isSwitch: true,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "regex",
|
||||||
|
shortcut: "re",
|
||||||
|
isSwitch: true,
|
||||||
|
},
|
||||||
],
|
],
|
||||||
extra: {
|
extra: {
|
||||||
info: <CommandInfo>{
|
info: <CommandInfo>{
|
||||||
|
@ -453,6 +468,7 @@ export class UtilityPlugin extends ZeppelinPlugin<TConfigSchema> {
|
||||||
"case-sensitive"?: boolean;
|
"case-sensitive"?: boolean;
|
||||||
export?: boolean;
|
export?: boolean;
|
||||||
ids?: boolean;
|
ids?: boolean;
|
||||||
|
regex?: boolean;
|
||||||
},
|
},
|
||||||
) {
|
) {
|
||||||
const formatSearchResultList = (members: Member[]): string => {
|
const formatSearchResultList = (members: Member[]): string => {
|
||||||
|
@ -473,7 +489,17 @@ export class UtilityPlugin extends ZeppelinPlugin<TConfigSchema> {
|
||||||
// If we're exporting the results, we don't need all the fancy schmancy pagination stuff.
|
// If we're exporting the results, we don't need all the fancy schmancy pagination stuff.
|
||||||
// Just get the results and dump them in an archive.
|
// Just get the results and dump them in an archive.
|
||||||
if (args.export) {
|
if (args.export) {
|
||||||
const results = await this.performMemberSearch(args, 1, SEARCH_EXPORT_LIMIT);
|
let results;
|
||||||
|
try {
|
||||||
|
results = await this.performMemberSearch(args, 1, SEARCH_EXPORT_LIMIT);
|
||||||
|
} catch (e) {
|
||||||
|
if (e instanceof SearchError) {
|
||||||
|
return this.sendErrorMessage(msg.channel, e.message);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
|
||||||
if (results.totalResults === 0) {
|
if (results.totalResults === 0) {
|
||||||
return this.sendErrorMessage(msg.channel, "No results found");
|
return this.sendErrorMessage(msg.channel, "No results found");
|
||||||
}
|
}
|
||||||
|
@ -519,7 +545,17 @@ export class UtilityPlugin extends ZeppelinPlugin<TConfigSchema> {
|
||||||
searchMsgPromise.then(m => (originalSearchMsg = m));
|
searchMsgPromise.then(m => (originalSearchMsg = m));
|
||||||
}
|
}
|
||||||
|
|
||||||
const searchResult = await this.performMemberSearch(args, page, perPage);
|
let searchResult;
|
||||||
|
try {
|
||||||
|
searchResult = await this.performMemberSearch(args, page, perPage);
|
||||||
|
} catch (e) {
|
||||||
|
if (e instanceof SearchError) {
|
||||||
|
return this.sendErrorMessage(msg.channel, e.message);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
|
||||||
if (searchResult.totalResults === 0) {
|
if (searchResult.totalResults === 0) {
|
||||||
return this.sendErrorMessage(msg.channel, "No results found");
|
return this.sendErrorMessage(msg.channel, "No results found");
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue