mirror of
https://github.com/xHyroM/bun-discord-bot.git
synced 2024-12-22 12:11:06 +01:00
feat: tag system
This commit is contained in:
parent
d2ef2e9050
commit
f3d0fad63d
6 changed files with 161 additions and 14 deletions
|
@ -1,7 +1,7 @@
|
|||
import { SlashCommandStringOption, SlashCommandUserOption } from "discord.js";
|
||||
import { defineCommand } from "../loaders/commands";
|
||||
import { AutocompleteContext } from "../structs/context/AutocompleteContext";
|
||||
import { InteractionCommandContext } from "../structs/context/CommandContext";
|
||||
import { defineCommand } from "../loaders/commands.ts";
|
||||
import { AutocompleteContext } from "../structs/context/AutocompleteContext.ts";
|
||||
import { InteractionCommandContext } from "../structs/context/CommandContext.ts";
|
||||
import algoliasearch from "algoliasearch";
|
||||
|
||||
const algoliaClient = algoliasearch("2527C13E0N", "4efc87205e1fce4a1f267cadcab42cb2");
|
||||
|
@ -74,7 +74,6 @@ defineCommand({
|
|||
content,
|
||||
allowedMentions: {
|
||||
parse: [ "users" ],
|
||||
repliedUser: true,
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import "./version.ts";
|
||||
import "./docs.ts";
|
||||
import "./tag.ts";
|
||||
|
||||
import { registerCommands } from "../loaders/commands.ts";
|
||||
await registerCommands();
|
||||
|
|
87
src/commands/tag.ts
Normal file
87
src/commands/tag.ts
Normal file
|
@ -0,0 +1,87 @@
|
|||
import { SlashCommandStringOption, SlashCommandUserOption, User } from "discord.js";
|
||||
import { defineCommand } from "../loaders/commands.ts";
|
||||
import { AutocompleteContext } from "../structs/context/AutocompleteContext.ts";
|
||||
import { getTags, searchTag } from "../loaders/tags.ts";
|
||||
import { InteractionCommandContext, MessageCommandContext } from "../structs/context/CommandContext.ts";
|
||||
import { Bubu } from "../structs/Client.ts";
|
||||
|
||||
defineCommand({
|
||||
name: "tag",
|
||||
description: "Get tag",
|
||||
options: [
|
||||
{
|
||||
...new SlashCommandStringOption()
|
||||
.setName("query")
|
||||
.setRequired(true)
|
||||
.setAutocomplete(true)
|
||||
.setDescription("Select query")
|
||||
.toJSON(),
|
||||
run: async(context: AutocompleteContext) => {
|
||||
const query = context.options.getString("query");
|
||||
if (!query) {
|
||||
return context.respond(getTags(25));
|
||||
}
|
||||
|
||||
const tags = searchTag(query, true);
|
||||
if (tags.length > 0)
|
||||
return context.respond(tags);
|
||||
|
||||
return context.respond(getTags(25));
|
||||
},
|
||||
},
|
||||
{
|
||||
...new SlashCommandUserOption()
|
||||
.setName("target")
|
||||
.setRequired(false)
|
||||
.setDescription("User to mention")
|
||||
.toJSON()
|
||||
}
|
||||
],
|
||||
run: (ctx: InteractionCommandContext) => {
|
||||
const query = ctx.interaction.options.getString("query");
|
||||
const target = ctx.interaction.options.getUser("target");
|
||||
|
||||
const tag = searchTag(query, false);
|
||||
if (!tag) {
|
||||
return ctx.reply({
|
||||
content: `\`❌\` Could not find a tag \`${query}\``,
|
||||
ephemeral: true,
|
||||
});
|
||||
}
|
||||
|
||||
ctx.reply({
|
||||
content: [
|
||||
target ? `*Suggestion for <@${target.id}>:*\n` : "",
|
||||
`**${tag.question}**`,
|
||||
tag.answer
|
||||
].join("\n"),
|
||||
allowedMentions: {
|
||||
parse: [ "users" ]
|
||||
}
|
||||
});
|
||||
},
|
||||
runMessage: (ctx: MessageCommandContext) => {
|
||||
const keyword = ctx.options?.[0] ?? "what-is-bun";
|
||||
|
||||
const target = ctx.options?.[1]?.match(/([0-9]+)/)?.[0];
|
||||
const resolvedTarget = target ? Bubu.users.cache.get(target) : null;
|
||||
|
||||
const tag = searchTag(keyword, false);
|
||||
if (!keyword || !tag) {
|
||||
return ctx.reply({
|
||||
content: `\`❌\` Could not find a tag \`${keyword}\``,
|
||||
});
|
||||
}
|
||||
|
||||
ctx.reply({
|
||||
content: [
|
||||
resolvedTarget ? `*Suggestion for <@${resolvedTarget.id}>:*\n` : "",
|
||||
`**${tag.question}**`,
|
||||
tag.answer
|
||||
].join("\n"),
|
||||
allowedMentions: {
|
||||
parse: [ "users" ]
|
||||
}
|
||||
});
|
||||
}
|
||||
})
|
|
@ -3,6 +3,8 @@ import { readFileSync } from "node:fs";
|
|||
import { globSync as glob } from "glob";
|
||||
import { join } from "node:path";
|
||||
import { Tag } from "../structs/Tag";
|
||||
import { APIApplicationCommandOptionChoice } from "discord.js";
|
||||
import { safeSlice } from "../util";
|
||||
|
||||
const tags = glob(join(__dirname, "..", "..", "data", "tags", "*.md"));
|
||||
|
||||
|
@ -18,3 +20,65 @@ for (const tag of tags) {
|
|||
answer: frontMatter.content
|
||||
});
|
||||
}
|
||||
|
||||
export function getTags(length: number): APIApplicationCommandOptionChoice[] {
|
||||
return safeSlice(
|
||||
TAGS.map((tag) => (
|
||||
{
|
||||
name: `🚀 ${tag.question}`,
|
||||
value: tag.question
|
||||
}
|
||||
)),
|
||||
length);
|
||||
}
|
||||
|
||||
export function searchTag<T extends boolean>(providedQuery: string, multiple?: T): T extends true ? APIApplicationCommandOptionChoice[] : Tag {
|
||||
const query = providedQuery?.toLowerCase()?.replace(/-/g, " ");
|
||||
|
||||
if (!multiple) {
|
||||
const exactKeyword = TAGS.find(tag => tag.keywords.find((k) => k.toLowerCase() === query));
|
||||
const keywordMatch = TAGS.find(tag => tag.keywords.find((k) => k.toLowerCase().includes(query)));
|
||||
const questionMatch = TAGS.find(tag => tag.question.toLowerCase().includes(query));
|
||||
const answerMatch = TAGS.find(tag => tag.answer.toLowerCase().includes(query));
|
||||
|
||||
const tag = exactKeyword ?? questionMatch ?? keywordMatch ?? answerMatch;
|
||||
return tag as T extends true ? APIApplicationCommandOptionChoice[] : Tag;
|
||||
}
|
||||
|
||||
const exactKeywords: APIApplicationCommandOptionChoice[] = [];
|
||||
const keywordMatches: APIApplicationCommandOptionChoice[] = [];
|
||||
const questionMatches: APIApplicationCommandOptionChoice[] = [];
|
||||
const answerMatches: APIApplicationCommandOptionChoice[] = [];
|
||||
|
||||
for (const tag of TAGS) {
|
||||
const exactKeyword = tag.keywords.find((t) => t.toLowerCase() === query);
|
||||
const includesKeyword = tag.keywords.find((t) => t.toLowerCase().includes(query));
|
||||
const questionMatch = tag.question.toLowerCase().includes(query);
|
||||
const answerMatch = tag.answer.toLowerCase().includes(query);
|
||||
|
||||
if (exactKeyword) {
|
||||
exactKeywords.push({
|
||||
name: `✅ ${tag.question}`,
|
||||
value: tag.question
|
||||
});
|
||||
} else if (includesKeyword) {
|
||||
keywordMatches.push({
|
||||
name: `🔑 ${tag.question}`,
|
||||
value: tag.question,
|
||||
})
|
||||
} else if(questionMatch) {
|
||||
questionMatches.push({
|
||||
name: `❓ ${tag.question}`,
|
||||
value: tag.question
|
||||
})
|
||||
} else if (answerMatch) {
|
||||
answerMatches.push({
|
||||
name: `📄 ${tag.question}`,
|
||||
value: tag.question
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const tags = [...exactKeywords, ...questionMatches, ...keywordMatches, ...answerMatches];
|
||||
return tags as T extends true ? APIApplicationCommandOptionChoice[] : Tag;
|
||||
}
|
||||
|
|
|
@ -3,7 +3,6 @@ import type { Command } from "../Command.ts";
|
|||
|
||||
export interface CommandContext<T extends boolean> {
|
||||
command: Command;
|
||||
isInteraction: T;
|
||||
|
||||
user: User;
|
||||
member: GuildMember | APIInteractionGuildMember;
|
||||
|
@ -21,10 +20,6 @@ export class InteractionCommandContext implements CommandContext<true> {
|
|||
this.interaction = interaction;
|
||||
}
|
||||
|
||||
get isInteraction(): true {
|
||||
return true;
|
||||
}
|
||||
|
||||
get user() {
|
||||
return this.interaction.user;
|
||||
}
|
||||
|
@ -49,16 +44,14 @@ export class InteractionCommandContext implements CommandContext<true> {
|
|||
export class MessageCommandContext implements CommandContext<false> {
|
||||
public command: Command;
|
||||
public message: Message;
|
||||
public options: string[];
|
||||
|
||||
public constructor(command: Command, message: Message, args: string[]) {
|
||||
this.command = command;
|
||||
this.message = message;
|
||||
|
||||
// change args structure to application commands like
|
||||
}
|
||||
|
||||
get isInteraction(): false {
|
||||
return false;
|
||||
// TODO: change args structure to application commands like
|
||||
this.options = args;
|
||||
}
|
||||
|
||||
get user() {
|
||||
|
|
3
src/util.ts
Normal file
3
src/util.ts
Normal file
|
@ -0,0 +1,3 @@
|
|||
export function safeSlice<T>(array: T[], length: number) {
|
||||
return array.length > length ? array.slice(0, length) : array;
|
||||
}
|
Loading…
Reference in a new issue