mirror of
https://github.com/ZeppelinBot/Zeppelin.git
synced 2025-03-15 05:41:51 +00:00
Utility: add more options to !search, make the results list prettier
This commit is contained in:
parent
59204fbf51
commit
151971bb84
2 changed files with 114 additions and 21 deletions
|
@ -6,8 +6,10 @@ import {
|
|||
embedPadding,
|
||||
errorMessage,
|
||||
isSnowflake,
|
||||
multiSorter,
|
||||
noop,
|
||||
simpleClosestStringMatch,
|
||||
sorter,
|
||||
stripObjectToScalars,
|
||||
successMessage,
|
||||
trimLines,
|
||||
|
@ -177,18 +179,59 @@ export class UtilityPlugin extends ZeppelinPlugin {
|
|||
msg.channel.createMessage(`The permission level of ${member.username}#${member.discriminator} is **${level}**`);
|
||||
}
|
||||
|
||||
@d.command("search", "<query:string$>")
|
||||
@d.command("search", "[query:string$]", {
|
||||
options: [
|
||||
{
|
||||
name: "page",
|
||||
type: "number",
|
||||
},
|
||||
{
|
||||
name: "role",
|
||||
type: "string",
|
||||
},
|
||||
{
|
||||
name: "voice",
|
||||
type: "bool",
|
||||
},
|
||||
{
|
||||
name: "sort",
|
||||
type: "string",
|
||||
},
|
||||
],
|
||||
})
|
||||
@d.permission("search")
|
||||
async searchCmd(msg: Message, args: { query: string }) {
|
||||
let [, query, inputPageNum] = args.query.match(/^(.*?)(?:\s([0-9]+))?$/);
|
||||
query = query.toLowerCase();
|
||||
async searchCmd(
|
||||
msg: Message,
|
||||
args: { query?: string; role?: string; page?: number; voice?: boolean; sort?: string },
|
||||
) {
|
||||
let matchingMembers = Array.from(this.guild.members.values());
|
||||
|
||||
const matchingMembers = this.guild.members.filter(member => {
|
||||
const fullUsername = `${member.user.username}#${member.user.discriminator}`;
|
||||
if (member.nick && member.nick.toLowerCase().indexOf(query) !== -1) return true;
|
||||
if (fullUsername.toLowerCase().indexOf(query) !== -1) return true;
|
||||
return false;
|
||||
});
|
||||
if (args.role) {
|
||||
const roleIds = args.role.split(",");
|
||||
matchingMembers = matchingMembers.filter(member => {
|
||||
for (const role of roleIds) {
|
||||
if (!member.roles.includes(role)) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
if (args.voice) {
|
||||
matchingMembers = matchingMembers.filter(m => m.voiceState.channelID != null);
|
||||
}
|
||||
|
||||
if (args.query) {
|
||||
let [, query] = args.query.match(/^(.*?)(?:\s([0-9]+))?$/);
|
||||
query = query.toLowerCase();
|
||||
|
||||
matchingMembers = matchingMembers.filter(member => {
|
||||
const fullUsername = `${member.user.username}#${member.user.discriminator}`;
|
||||
if (member.nick && member.nick.toLowerCase().indexOf(query) !== -1) return true;
|
||||
if (fullUsername.toLowerCase().indexOf(query) !== -1) return true;
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
if (matchingMembers.length > 0) {
|
||||
let header;
|
||||
|
@ -196,8 +239,7 @@ export class UtilityPlugin extends ZeppelinPlugin {
|
|||
|
||||
const paginated = matchingMembers.length > MAX_SEARCH_RESULTS;
|
||||
|
||||
const pageInputMatch = args.query.match(/\s([0-9]+)$/);
|
||||
const inputPage = pageInputMatch ? parseInt(pageInputMatch[1], 10) : 1;
|
||||
const inputPage = args.page || 1;
|
||||
const lastPage = Math.ceil(matchingMembers.length / MAX_SEARCH_RESULTS);
|
||||
const page = Math.min(lastPage, Math.max(1, inputPage));
|
||||
|
||||
|
@ -210,17 +252,28 @@ export class UtilityPlugin extends ZeppelinPlugin {
|
|||
header = `Found ${matchingMembers.length} ${resultText}`;
|
||||
}
|
||||
|
||||
let lines = matchingMembers.map(member => {
|
||||
return `${member.user.username}#${member.user.discriminator} (${member.id})`;
|
||||
});
|
||||
lines.sort((a, b) => {
|
||||
return a.toLowerCase() < b.toLowerCase() ? -1 : 1;
|
||||
});
|
||||
lines = lines.slice(from, to);
|
||||
const pageMembers = matchingMembers.slice(from, to);
|
||||
|
||||
const footer = paginated ? "Add a page number to the end of the command to browse results" : "";
|
||||
const [, sortDir, sortBy] = args.sort ? args.sort.match(/^(-?)(.*)$/) : [null, "ASC", "name"];
|
||||
const realSortDir = sortDir === "-" ? "DESC" : "ASC";
|
||||
|
||||
msg.channel.createMessage(`${header}\n\`\`\`${lines.join("\n")}\`\`\`${footer}`);
|
||||
if (sortBy === "id") {
|
||||
pageMembers.sort(sorter(m => BigInt(m.id), realSortDir));
|
||||
} else {
|
||||
pageMembers.sort(
|
||||
multiSorter([[m => m.username.toLowerCase(), realSortDir], [m => m.discriminator, realSortDir]]),
|
||||
);
|
||||
}
|
||||
|
||||
const longestId = pageMembers.reduce((longest, member) => Math.max(longest, member.id.length), 0);
|
||||
const lines = pageMembers.map(member => {
|
||||
const paddedId = member.id.padEnd(longestId, " ");
|
||||
return `${paddedId} ${member.user.username}#${member.user.discriminator}`;
|
||||
});
|
||||
|
||||
const footer = paginated ? "Use --page=n to browse results" : "";
|
||||
|
||||
msg.channel.createMessage(`${header}\n\`\`\`js\n${lines.join("\n")}\`\`\`${footer}`);
|
||||
} else {
|
||||
msg.channel.createMessage(errorMessage("No results found"));
|
||||
}
|
||||
|
|
40
src/utils.ts
40
src/utils.ts
|
@ -383,6 +383,46 @@ export function simpleClosestStringMatch<T>(searchStr, haystack: T[], getter = n
|
|||
return itemsWithRankings[0][0];
|
||||
}
|
||||
|
||||
type sorterDirection = "ASC" | "DESC";
|
||||
type sorterGetterFn = (any) => any;
|
||||
type sorterGetterFnWithDirection = [sorterGetterFn, sorterDirection];
|
||||
type sorterGetterResolvable = string | sorterGetterFn;
|
||||
type sorterGetterResolvableWithDirection = [sorterGetterResolvable, sorterDirection];
|
||||
type sorterFn = (a: any, b: any) => number;
|
||||
|
||||
function resolveGetter(getter: sorterGetterResolvable): sorterGetterFn {
|
||||
if (typeof getter === "string") {
|
||||
return obj => obj[getter];
|
||||
}
|
||||
|
||||
return getter;
|
||||
}
|
||||
|
||||
export function multiSorter(getters: Array<sorterGetterResolvable | sorterGetterResolvableWithDirection>): sorterFn {
|
||||
const resolvedGetters: sorterGetterFnWithDirection[] = getters.map(getter => {
|
||||
if (Array.isArray(getter)) {
|
||||
return [resolveGetter(getter[0]), getter[1]] as sorterGetterFnWithDirection;
|
||||
} else {
|
||||
return [resolveGetter(getter), "ASC"] as sorterGetterFnWithDirection;
|
||||
}
|
||||
});
|
||||
|
||||
return (a, b) => {
|
||||
for (const getter of resolvedGetters) {
|
||||
const aVal = getter[0](a);
|
||||
const bVal = getter[0](b);
|
||||
if (aVal > bVal) return getter[1] === "ASC" ? 1 : -1;
|
||||
if (aVal < bVal) return getter[1] === "ASC" ? -1 : 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
};
|
||||
}
|
||||
|
||||
export function sorter(getter: sorterGetterResolvable, direction: sorterDirection = "ASC"): sorterFn {
|
||||
return multiSorter([[getter, direction]]);
|
||||
}
|
||||
|
||||
export function noop() {
|
||||
// IT'S LITERALLY NOTHING
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue