mirror of
https://github.com/xHyroM/bun-discord-bot.git
synced 2024-11-22 14:41:05 +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 { SlashCommandStringOption, SlashCommandUserOption } from "discord.js";
|
||||||
import { defineCommand } from "../loaders/commands";
|
import { defineCommand } from "../loaders/commands.ts";
|
||||||
import { AutocompleteContext } from "../structs/context/AutocompleteContext";
|
import { AutocompleteContext } from "../structs/context/AutocompleteContext.ts";
|
||||||
import { InteractionCommandContext } from "../structs/context/CommandContext";
|
import { InteractionCommandContext } from "../structs/context/CommandContext.ts";
|
||||||
import algoliasearch from "algoliasearch";
|
import algoliasearch from "algoliasearch";
|
||||||
|
|
||||||
const algoliaClient = algoliasearch("2527C13E0N", "4efc87205e1fce4a1f267cadcab42cb2");
|
const algoliaClient = algoliasearch("2527C13E0N", "4efc87205e1fce4a1f267cadcab42cb2");
|
||||||
|
@ -74,7 +74,6 @@ defineCommand({
|
||||||
content,
|
content,
|
||||||
allowedMentions: {
|
allowedMentions: {
|
||||||
parse: [ "users" ],
|
parse: [ "users" ],
|
||||||
repliedUser: true,
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import "./version.ts";
|
import "./version.ts";
|
||||||
import "./docs.ts";
|
import "./docs.ts";
|
||||||
|
import "./tag.ts";
|
||||||
|
|
||||||
import { registerCommands } from "../loaders/commands.ts";
|
import { registerCommands } from "../loaders/commands.ts";
|
||||||
await registerCommands();
|
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 { globSync as glob } from "glob";
|
||||||
import { join } from "node:path";
|
import { join } from "node:path";
|
||||||
import { Tag } from "../structs/Tag";
|
import { Tag } from "../structs/Tag";
|
||||||
|
import { APIApplicationCommandOptionChoice } from "discord.js";
|
||||||
|
import { safeSlice } from "../util";
|
||||||
|
|
||||||
const tags = glob(join(__dirname, "..", "..", "data", "tags", "*.md"));
|
const tags = glob(join(__dirname, "..", "..", "data", "tags", "*.md"));
|
||||||
|
|
||||||
|
@ -18,3 +20,65 @@ for (const tag of tags) {
|
||||||
answer: frontMatter.content
|
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> {
|
export interface CommandContext<T extends boolean> {
|
||||||
command: Command;
|
command: Command;
|
||||||
isInteraction: T;
|
|
||||||
|
|
||||||
user: User;
|
user: User;
|
||||||
member: GuildMember | APIInteractionGuildMember;
|
member: GuildMember | APIInteractionGuildMember;
|
||||||
|
@ -21,10 +20,6 @@ export class InteractionCommandContext implements CommandContext<true> {
|
||||||
this.interaction = interaction;
|
this.interaction = interaction;
|
||||||
}
|
}
|
||||||
|
|
||||||
get isInteraction(): true {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
get user() {
|
get user() {
|
||||||
return this.interaction.user;
|
return this.interaction.user;
|
||||||
}
|
}
|
||||||
|
@ -49,16 +44,14 @@ export class InteractionCommandContext implements CommandContext<true> {
|
||||||
export class MessageCommandContext implements CommandContext<false> {
|
export class MessageCommandContext implements CommandContext<false> {
|
||||||
public command: Command;
|
public command: Command;
|
||||||
public message: Message;
|
public message: Message;
|
||||||
|
public options: string[];
|
||||||
|
|
||||||
public constructor(command: Command, message: Message, args: string[]) {
|
public constructor(command: Command, message: Message, args: string[]) {
|
||||||
this.command = command;
|
this.command = command;
|
||||||
this.message = message;
|
this.message = message;
|
||||||
|
|
||||||
// change args structure to application commands like
|
// TODO: change args structure to application commands like
|
||||||
}
|
this.options = args;
|
||||||
|
|
||||||
get isInteraction(): false {
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get user() {
|
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