refactor: change to advanced handlers (#36)

Since simple handlers are getting deprecated and soon removed im making
this PR to update the bot.

There are some "issues" regarding message commands, given that im not
sure yet if advanced handlers will ever have message commands, i will
update on the matter when i can, so for now we still use simple handlers
for that.
This commit is contained in:
DidaS 2024-07-19 22:10:48 +01:00 committed by GitHub
parent 53f730b7aa
commit b0438f26c0
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
20 changed files with 251 additions and 265 deletions

BIN
bun.lockb

Binary file not shown.

View file

@ -11,15 +11,15 @@
"bun-types": "^1.1.6"
},
"dependencies": {
"@lilybird/handlers": "^0.4.0",
"@lilybird/jsx": "0.2.0",
"@lilybird/transformers": "^0.2.0",
"@lilybird/handlers": "^0.6.0-beta.9",
"@lilybird/jsx": "0.3.0",
"@lilybird/transformers": "^0.4.1",
"@paperdave/logger": "^3.0.1",
"@purplet/serialize": "^2.0.0",
"@wolfram-alpha/wolfram-alpha-api": "^23.1004.144821-RELEASE",
"algoliasearch": "^4.23.2",
"bun-tracestrings": "github:oven-sh/bun.report",
"gray-matter": "^4.0.3",
"lilybird": "^0.7.1-alpha.0"
"lilybird": "^0.7.3"
}
}

View file

@ -1,9 +1,5 @@
import {
ApplicationCommand as JSXApplicationCommand,
StringOption,
UserOption,
} from "@lilybird/jsx";
import { ApplicationCommand } from "@lilybird/handlers";
import { AllowedMentionType, ApplicationCommandOptionType } from "lilybird";
import { $applicationCommand } from "@lilybird/handlers/advanced";
import algoliasearch from "algoliasearch";
import { safeSlice } from "src/util.ts";
@ -14,19 +10,22 @@ const algoliaClient = algoliasearch(
);
const algoliaIndex = algoliaClient.initIndex("bun");
export default {
post: "GLOBAL",
data: (
<JSXApplicationCommand name="docs" description="Search at docs">
<StringOption
name="query"
description="Select query"
required
autocomplete
/>
<UserOption name="target" description="User to mention" />
</JSXApplicationCommand>
),
$applicationCommand({
name: "docs",
description: "Search at docs",
options: [
{
type: ApplicationCommandOptionType.STRING,
name: "query",
description: "Select query",
required: true,
autocomplete: true,
}, {
type: ApplicationCommandOptionType.USER,
name: "target",
description: "User to mention"
}
],
autocomplete: async (interaction) => {
const query = interaction.data.getFocused<string>().value;
const result = await algoliaIndex.search(query, {
@ -44,7 +43,7 @@ export default {
})
);
},
run: async (interaction) => {
handle: async (interaction) => {
await interaction.deferReply();
const query = interaction.data.getString("query");
@ -75,15 +74,14 @@ export default {
: "",
].join("\n");
// @ts-expect-error allowed_mentions
await interaction.editReply({
content,
allowed_mentions: {
parse: target ? ["users"] : [],
parse: target ? [AllowedMentionType.UserMentions] : [],
},
});
},
} satisfies ApplicationCommand;
});
function getHitName(hit: any) {
const type = hit.hierarchy.lvl0 === "Documentation" ? "📖" : "🗺️";

View file

@ -1,10 +1,6 @@
import {
ApplicationCommand as JSXApplicationCommand,
BooleanOption,
CommandOptions,
StringOption,
} from "@lilybird/jsx";
import { ApplicationCommand } from "@lilybird/handlers";
import { $applicationCommand } from "@lilybird/handlers/advanced";
import { ApplicationCommandOptionType } from "lilybird";
import { safeSlice, silently } from "../util.ts";
type State =
@ -37,56 +33,57 @@ interface Item {
};
}
export default {
post: "GLOBAL",
data: (
<JSXApplicationCommand
name="github"
description="Query an issue, pull request or direct link to issue, pull request"
>
<StringOption
name="query"
description="Issue/Pull request number or name"
autocomplete
required
max_length={100}
/>
<StringOption name="state" description="Issue or Pull request state">
<CommandOptions name="🔴🟠 Open" value="open" />
<CommandOptions
name="🟢 Closed as completed"
value="closed_as_completed"
/>
<CommandOptions
name="⚪️ Closed as not planned"
value="closed_as_not_planned"
/>
<CommandOptions name="⚫️ Closed" value="closed" />
<CommandOptions name="🟣 Merged" value="merged" />
<CommandOptions name="📝 Draft" value="draft" />
<CommandOptions name="🌍 All" value="all" />
</StringOption>
<StringOption name="type" description="Issue/Pull request number or name">
<CommandOptions name="🐛 Issues" value="issues" />
<CommandOptions name="🔨 Pull Requests" value="pull_requests" />
<CommandOptions name="🌍 Both" value="both" />
</StringOption>
<BooleanOption name="hide" description="Show this message only for you" />
</JSXApplicationCommand>
),
run: async (interaction) => {
$applicationCommand({
name: "github",
description: "Query an issue, pull request or direct link to issue, pull request",
options: [
{
type: ApplicationCommandOptionType.STRING,
name: "query",
description: "Issue/Pull request number or name",
autocomplete: true,
required: true,
max_length: 100
}, {
type: ApplicationCommandOptionType.STRING,
name: "state",
description: "Issue or Pull request state",
choices: [
{ name: "🔴🟠 Open", value: "open" },
{ name: "🟢 Closed as completed", value: "closed_as_completed" },
{ name: "⚪️ Closed as not planned", value: "closed_as_not_planned" },
{ name: "⚫️ Closed", value: "closed" },
{ name: "🟣 Merged", value: "merged" },
{ name: "📝 Draft", value: "draft" },
{ name: "🌍 All", value: "all" }
]
}, {
type: ApplicationCommandOptionType.STRING,
name: "type",
description: "Issue/Pull request number or name",
choices: [
{ name: "🐛 Issues", value: "issues" },
{ name: "🔨 Pull Requests", value: "pull_requests" },
{ name: "🌍 Both", value: "both" }
],
},
{
type: ApplicationCommandOptionType.BOOLEAN,
name: "hide",
description: "Show this message only for you"
}
],
handle: async (interaction) => {
const hide = interaction.data.getBoolean("hide") ?? false;
await interaction.deferReply(hide);
const query = interaction.data.getString("query", true);
const state: State =
(interaction.data.getString("state") as State) || "all";
const state: State = (interaction.data.getString("state") as State) || "all";
const type: Type = (interaction.data.getString("type") as Type) || "both";
const result = (await search(query, state, type))[0];
if (!result) {
// @ts-expect-error allowed_mentions
interaction.editReply({
content: `❌ Couldn't find issue or pull request \`${query}\``,
allowed_mentions: {
@ -96,13 +93,10 @@ export default {
return;
}
// @ts-expect-error allowed_mentions
interaction.editReply({
content: [
`${result.emoji.type} ${result.emoji.state} [#${
result.number
} in oven-sh/bun](<${result.html_url}>) by [${result.user.login}](<${
result.user.html_url
`${result.emoji.type} ${result.emoji.state} [#${result.number
} in oven-sh/bun](<${result.html_url}>) by [${result.user.login}](<${result.user.html_url
}>) ${stateToText(result)} ${stateToTimestamp(result)}`,
result.title,
].join("\n"),
@ -111,7 +105,6 @@ export default {
},
});
},
autocomplete: async (interaction) => {
const query = interaction.data.getFocused<string>().value;
const state: State =
@ -132,7 +125,7 @@ export default {
)
);
},
} satisfies ApplicationCommand;
});
function stateToText(item: Item) {
switch (item.emoji.state) {

View file

@ -1,13 +1,7 @@
// https://github.com/discordjs/discord-utils-bot/blob/main/src/functions/autocomplete/mdnAutoComplete.ts#L23-L47 thanks
// https://github.com/discordjs/discord-utils-bot/blob/main/src/functions/mdn.ts#L59C1-L78C3 thanks
import {
BooleanOption,
ApplicationCommand as JSXApplicationCommand,
StringOption,
UserOption,
} from "@lilybird/jsx";
import { ApplicationCommand } from "@lilybird/handlers";
import { ApplicationCommandOptionType, AllowedMentionType } from "lilybird";
import { $applicationCommand } from "@lilybird/handlers/advanced";
import { MDN_API, MDN_DISCORD_EMOJI } from "src/constants.ts";
import { safeSlice, silently } from "src/util.ts";
@ -43,25 +37,29 @@ const MDN_DATA = (await (
const cache = new Map<string, Document>();
export default {
post: "GLOBAL",
data: (
<JSXApplicationCommand
name="mdn"
description="Search the Mozilla Developer Network documentation"
>
<StringOption
name="query"
description="Class or method to search for"
required
autocomplete
max_length={100}
/>
<UserOption name="target" description="User to mention" />
<BooleanOption name="hide" description="Show this message only for you" />
</JSXApplicationCommand>
),
run: async (interaction) => {
$applicationCommand({
name: "mdn",
description: "Search the Mozilla Developer Network documentation",
options: [
{
type: ApplicationCommandOptionType.STRING,
name: "query",
description: "Class or method to search for",
required: true,
autocomplete: true,
max_length: 100
}, {
type: ApplicationCommandOptionType.USER,
name: "target",
description: "User to mention"
}, {
type: ApplicationCommandOptionType.BOOLEAN,
name: "hide",
description: "Show this message only for you"
}
],
handle: async (interaction) => {
const hide = interaction.data.getBoolean("hide") ?? false;
await interaction.deferReply(hide);
@ -101,14 +99,13 @@ export default {
intro,
];
// @ts-expect-error allowed_mentions
await interaction.editReply({
content: [
target ? `*Suggestion for <@${target}>:*\n` : "",
parts.join("\n"),
].join("\n"),
allowed_mentions: {
parse: target ? ["users"] : [],
parse: target ? [AllowedMentionType.UserMentions] : [],
},
});
},
@ -151,7 +148,7 @@ export default {
)
);
},
} satisfies ApplicationCommand;
});
function escape(text: string) {
return text.replaceAll("||", "|\u200B|").replaceAll("*", "\\*");

View file

@ -1,17 +1,42 @@
import {
ActionRow,
Button,
ApplicationCommand as JSXApplicationCommand,
} from "@lilybird/jsx";
import { ApplicationCommand } from "@lilybird/handlers";
import { $applicationCommand } from "@lilybird/handlers/advanced";
import { serializers as S } from "@purplet/serialize";
import { possibleClosedForm } from "../util.ts";
import { ButtonStyle } from "lilybird";
import { ActionRow, Button } from "@lilybird/jsx";
import { possibleClosedForm, silently } from "../util.ts";
import { ButtonStyle, ComponentType } from "lilybird";
export default {
post: "GLOBAL",
data: <JSXApplicationCommand name="ping" description="pong" />,
run: async (interaction) => {
$applicationCommand({
name: "ping",
description: "pong",
components: [{
type: ComponentType.Button,
id: "ping",
customMatcher: `custom_id[0] == "0" && custom_id[1] == "-"`,
handle: (interaction) => {
const combined = interaction.data.id.split("-")?.[1];
if (!combined) return;
const [ws, wsClosedForm, rest, restClosedForm] = S.generic.decodeCustomId(combined);
silently(
interaction.reply({
content: [
`🏓`,
"**WebSocket:**",
`\`${wsClosedForm}\``,
`\`${ws} ms\``,
"",
"**Rest:**",
`\`${restClosedForm}\``,
`\`${rest} ms\``,
"",
"Mathematics is the language of the universe, it's truly fascinating! 😄",
].join("\n"),
ephemeral: true,
})
);
}
}],
handle: async (interaction) => {
await interaction.deferReply();
const { ws, rest } = await interaction.client.ping();
@ -46,4 +71,4 @@ export default {
],
});
},
} satisfies ApplicationCommand;
});

View file

@ -1,32 +1,32 @@
import {
ApplicationCommand as JSXApplicationCommand,
StringOption,
UserOption,
} from "@lilybird/jsx";
import { AllowedMentionType, ApplicationCommandOptionType } from "lilybird";
import { $applicationCommand } from "@lilybird/handlers/advanced";
import { getTags, searchTag } from "../loaders/tags.ts";
import { ApplicationCommand } from "@lilybird/handlers";
export default {
post: "GLOBAL",
data: (
<JSXApplicationCommand name="tag" description="Get tag">
<StringOption
name="query"
description="Select query"
required
autocomplete
/>
<UserOption name="target" description="User to mention" />
</JSXApplicationCommand>
),
run: async (interaction) => {
$applicationCommand({
name: "tag",
description: "Get tag",
options: [
{
type: ApplicationCommandOptionType.STRING,
name: "query",
description: "Select query",
required: true,
autocomplete: true
},
{
type: ApplicationCommandOptionType.USER,
name: "target",
description: "User to mention"
}
],
handle: async (interaction) => {
if (!interaction.inGuild()) return;
const query = interaction.data.getString("query", true);
const target = interaction.data.getUser("target");
const tag = searchTag(interaction.channel, query, false);
if (!tag) {
// @ts-expect-error allowed_mentions
return interaction.reply({
content: `\`\` Could not find a tag \`${query}\``,
ephemeral: true,
@ -36,7 +36,6 @@ export default {
});
}
// @ts-expect-error allowed_mentions
await interaction.reply({
content: [
target ? `*Suggestion for <@${target}>:*\n` : "",
@ -44,7 +43,7 @@ export default {
tag.answer,
].join("\n"),
allowed_mentions: {
parse: target ? ["users"] : [],
parse: target ? [AllowedMentionType.UserMentions] : [],
},
});
},
@ -60,4 +59,4 @@ export default {
return await interaction.showChoices(getTags(interaction.channel, 25));
},
} satisfies ApplicationCommand;
});

View file

@ -1,3 +1,5 @@
import { $applicationCommand } from "@lilybird/handlers/advanced";
import {
COMMIT_HASH,
PRODUCTION,
@ -5,17 +7,14 @@ import {
LILYBIRD_HANDLERS_VERSION,
LILYBIRD_JSX_VERSION,
} from "../constants.ts";
import { ApplicationCommand as JSXApplicationCommand } from "@lilybird/jsx";
import { ApplicationCommand } from "@lilybird/handlers";
export default {
post: "GLOBAL",
data: <JSXApplicationCommand name="version" description="Show version" />,
run: (interaction) => {
$applicationCommand({
name: "version",
description: "Show version",
handle: (interaction) => {
interaction.reply({
content: [
`[git-bun-discord-bot-${COMMIT_HASH}](<https://github.com/xHyroM/bun-discord-bot/tree/${COMMIT_HASH}>) ${
!PRODUCTION ? "(dev)" : ""
`[git-bun-discord-bot-${COMMIT_HASH}](<https://github.com/xHyroM/bun-discord-bot/tree/${COMMIT_HASH}>) ${!PRODUCTION ? "(dev)" : ""
}`,
`[Bun v${Bun.version} (${Bun.revision})](<https://github.com/oven-sh/bun/releases/tag/bun-v${Bun.version}>)`,
"",
@ -26,4 +25,4 @@ export default {
ephemeral: true,
});
},
} satisfies ApplicationCommand;
});

12
src/handler.ts Normal file
View file

@ -0,0 +1,12 @@
import { defaultTransformers } from "@lilybird/transformers";
import { Handler } from "@lilybird/handlers/advanced";
const { interactionCreate, ...simpleTransformers } = defaultTransformers;
export type SimpleTransformers = typeof simpleTransformers;
export const transformers = simpleTransformers;
export const handler = new Handler<SimpleTransformers>({});
export const $applicationCommand = handler.storeCommand.bind(handler);
export const $listener = handler.storeListener.bind(handler);
export const $component = handler.buttonCollector.bind(handler);

View file

@ -1,21 +1,34 @@
import "./loaders/tags.ts";
import { SimpleTransformers, transformers, handler } from "./handler.ts";
import { ClientListeners, Intents, createClient } from "lilybird";
import { createHandler } from "@lilybird/handlers/simple";
import { createClient, Intents } from "lilybird";
import { info } from "@paperdave/logger"
// Make sure bubu will not crash
process.on("unhandledRejection", console.error);
process.on("uncaughtException", console.error);
const listeners = await createHandler({
handler.cachePath = `${import.meta.dir}/lily-cache/handler`;
await handler.scanDir(`${import.meta.dir}/commands`);
await handler.scanDir(`${import.meta.dir}/listeners`);
const { listeners: { messageCreate } } = await createHandler({
prefix: process.env.MESSAGE_PREFIX,
dirs: {
messageCommands: `${import.meta.dir}/message-commands`,
slashCommands: `${import.meta.dir}/commands`,
listeners: `${import.meta.dir}/listeners`,
messageCommands: `${import.meta.dir}/message-commands`
},
});
const listeners = handler.getListenersObject() as ClientListeners<SimpleTransformers>;
if (typeof listeners.messageCreate !== "undefined" && typeof messageCreate !== "undefined")
listeners.messageCreate = async (m) => { await listeners.messageCreate!(m); await messageCreate(m) }
else if (typeof messageCreate !== "undefined") listeners.messageCreate = messageCreate;
await createClient({
token: process.env.DISCORD_BOT_TOKEN,
intents: [
@ -24,5 +37,10 @@ await createClient({
Intents.MESSAGE_CONTENT,
Intents.GUILD_MEMBERS,
],
...listeners,
setup: async (client) => {
info(`Logged in as ${client.user.username} (${client.user.id})`);
await handler.loadGlobalCommands(client);
},
transformers,
listeners
});

View file

@ -1,42 +0,0 @@
import { Event } from "@lilybird/handlers";
import { serializers as S } from "@purplet/serialize";
import { silently } from "src/util.ts";
export default {
event: "interactionCreate",
run: (interaction) => {
if (
!interaction.isMessageComponentInteraction() ||
!interaction.data.isButton()
)
return;
const id = interaction.data.id;
if (id?.[0] == "0" && id?.[1] == "-") {
const combined = interaction.data.id.split("-")?.[1];
if (!combined) return;
const [ws, wsClosedForm, rest, restClosedForm] =
S.generic.decodeCustomId(combined);
silently(
interaction.reply({
content: [
`🏓`,
"**WebSocket:**",
`\`${wsClosedForm}\``,
`\`${ws} ms\``,
"",
"**Rest:**",
`\`${restClosedForm}\``,
`\`${rest} ms\``,
"",
"Mathematics is the language of the universe, it's truly fascinating! 😄",
].join("\n"),
ephemeral: true,
})
);
}
},
} satisfies Event<"interactionCreate">;

View file

@ -1,9 +1,9 @@
import { $listener } from "../handler.ts";
import { moderateNick } from "../util.ts";
import { Event } from "@lilybird/handlers";
export default {
$listener({
event: "guildMemberAdd",
run: (member) => {
handle: (member) => {
moderateNick(member);
},
} satisfies Event<"guildMemberAdd">;
});

View file

@ -1,9 +1,9 @@
import { $listener } from "../handler.ts";
import { moderateNick } from "../util.ts";
import { Event } from "@lilybird/handlers";
export default {
$listener({
event: "guildMemberUpdate",
run: (member) => {
handle: (member) => {
moderateNick(member);
},
} satisfies Event<"guildMemberUpdate">;
});

View file

@ -1,8 +1,9 @@
import { ButtonStyle } from "lilybird";
import { Message } from "@lilybird/transformers";
import { ActionRow, Button } from "@lilybird/jsx";
import { Message } from "@lilybird/transformers";
import { extname, basename } from "node:path";
import { Event } from "@lilybird/handlers";
import { $listener } from "../handler.ts";
import { ButtonStyle } from "lilybird";
import {
getBunReportDetailsInMarkdown,
getRandomBunEmoji,
@ -12,18 +13,16 @@ import {
const GITHUB_LINE_URL_REGEX =
/(?:https?:\/\/)?(?:www\.)?(?:github)\.com\/(?<repo>[a-zA-Z0-9-_]+\/[A-Za-z0-9_.-]+)\/blob\/(?<path>.+?)#L(?<first_line_number>\d+)[-~]?L?(?<second_line_number>\d*)/i;
const TWITTER_TWEET_URL_REGEX =
/https:\/\/(?:www\.)?(?:twitter|x)\.com\/(?<user>[a-zA-Z0-9-_]+)\/status\/(?<id>\d+)/i;
const BUN_REPORT_URL_REGEX = /(https:\/\/bun\.report\/\d+\.\d+(\.\d+)?\/\S+)/g;
export default {
$listener({
event: "messageCreate",
run: async (message) => {
handle: (message) => {
if (handleBunOnlyChannel(message)) return;
if (!message.content?.toLowerCase().startsWith(process.env.MESSAGE_PREFIX))
return handleOthers(message);
},
} satisfies Event<"messageCreate">;
});
function handleOthers(message: Message): void {
handleGithubLink(message);
@ -92,10 +91,8 @@ async function handleGithubLink(message: Message): Promise<void> {
if (extension === "zig") extension = "rs";
// @ts-expect-error allowed_mentions
message.reply({
content: `***${basename(path)}*** — *(L${firstLineNumber + 1}${
secondLineNumber ? `-L${secondLineNumber}` : ""
content: `***${basename(path)}*** — *(L${firstLineNumber + 1}${secondLineNumber ? `-L${secondLineNumber}` : ""
})*\n\`\`\`${extension}\n${safeSlice(
text,
2000 - 6 - extension.length
@ -104,8 +101,7 @@ async function handleGithubLink(message: Message): Promise<void> {
<ActionRow>
<Button
style={ButtonStyle.Link}
url={`https://github.com/${repo}/blob/${path}#L${
firstLineNumber + 1
url={`https://github.com/${repo}/blob/${path}#L${firstLineNumber + 1
}${secondLineNumber ? `-L${secondLineNumber}` : ""}`}
label={repo}
/>
@ -126,7 +122,6 @@ async function handleBunReportLink(message: Message): Promise<void> {
const data = await getBunReportDetailsInMarkdown(match[0]);
if (!data) return;
// @ts-expect-error allowed_mentions
message.reply({
content: data,
allowed_mentions: {

View file

@ -1,13 +1,14 @@
import { Event } from "@lilybird/handlers";
import { PartialMessage } from "@lilybird/transformers";
import { isBunOnlyLikeMessage } from "src/util.ts";
import { $listener } from "../handler.ts";
export default {
$listener({
event: "messageUpdate",
run: async (message) => {
handle: (message) => {
if (handleBunOnlyChannel(message)) return;
},
} satisfies Event<"messageUpdate">;
});
function handleBunOnlyChannel(message: PartialMessage): boolean {
if (message.channelId !== process.env.BUN_ONLY_CHANNEL_ID) return false;

View file

@ -1,9 +0,0 @@
import { info } from "@paperdave/logger";
import { Event } from "@lilybird/handlers";
export default {
event: "ready",
run: (client) => {
info(`Logged in as ${client.user.username} (${client.user.id})`);
},
} satisfies Event<"ready">;

View file

@ -1,5 +1,5 @@
import { ApplicationCommandOptionChoiceStructure } from "lilybird";
import { PartialChannel, GuildTextChannel } from "@lilybird/transformers";
import { ApplicationCommand } from "lilybird";
import { Tag } from "../structs/Tag.ts";
import { readFileSync } from "node:fs";
import { safeSlice } from "../util.ts";
@ -49,9 +49,9 @@ function getAvailableTags(channel: PartialGuildTextChannel) {
export function getTags(
channel: PartialGuildTextChannel,
length: number
): Array<ApplicationCommandOptionChoiceStructure> {
): Array<ApplicationCommand.Option.ChoiceStructure> {
const availableTags = getAvailableTags(channel);
return safeSlice<Array<ApplicationCommandOptionChoiceStructure>>(
return safeSlice<Array<ApplicationCommand.Option.ChoiceStructure>>(
availableTags.map((tag) => ({
name: `🚀 ${tag.question}`,
value: tag.question,
@ -64,7 +64,7 @@ export function searchTag<T extends boolean>(
channel: PartialGuildTextChannel,
providedQuery: string,
multiple?: T
): T extends true ? Array<ApplicationCommandOptionChoiceStructure> : Tag {
): T extends true ? Array<ApplicationCommand.Option.ChoiceStructure> : Tag {
const availableTags = getAvailableTags(channel);
const query = providedQuery?.toLowerCase()?.replace(/-/g, " ");
@ -84,14 +84,14 @@ export function searchTag<T extends boolean>(
const tag = exactKeyword ?? questionMatch ?? keywordMatch ?? answerMatch;
return tag as T extends true
? Array<ApplicationCommandOptionChoiceStructure>
? Array<ApplicationCommand.Option.ChoiceStructure>
: Tag;
}
const exactKeywords: Array<ApplicationCommandOptionChoiceStructure> = [];
const keywordMatches: Array<ApplicationCommandOptionChoiceStructure> = [];
const questionMatches: Array<ApplicationCommandOptionChoiceStructure> = [];
const answerMatches: Array<ApplicationCommandOptionChoiceStructure> = [];
const exactKeywords: Array<ApplicationCommand.Option.ChoiceStructure> = [];
const keywordMatches: Array<ApplicationCommand.Option.ChoiceStructure> = [];
const questionMatches: Array<ApplicationCommand.Option.ChoiceStructure> = [];
const answerMatches: Array<ApplicationCommand.Option.ChoiceStructure> = [];
for (const tag of availableTags) {
const exactKeyword = tag.keywords.find((t) => t.toLowerCase() === query);
@ -131,6 +131,6 @@ export function searchTag<T extends boolean>(
...answerMatches,
];
return tags as T extends true
? Array<ApplicationCommandOptionChoiceStructure>
? Array<ApplicationCommand.Option.ChoiceStructure>
: Tag;
}

View file

@ -1,5 +1,5 @@
import { MessageCommand } from "@lilybird/handlers/simple";
import { serializers as S } from "@purplet/serialize";
import { MessageCommand } from "@lilybird/handlers";
import { ActionRow, Button } from "@lilybird/jsx";
import { possibleClosedForm } from "../util.ts";
import { ButtonStyle } from "lilybird";

View file

@ -1,5 +1,6 @@
import { MessageCommand } from "@lilybird/handlers";
import { MessageCommand } from "@lilybird/handlers/simple";
import { searchTag } from "../loaders/tags.ts";
import { AllowedMentionType } from "lilybird";
export default {
name: "tag",
@ -15,7 +16,6 @@ export default {
const tag = searchTag(await message.fetchChannel(), keyword, false);
if (!keyword || !tag) return; // just ignore
// @ts-expect-error allowed_mentions
message.reply({
content: [
resolvedTarget ? `*Suggestion for <@${resolvedTarget.id}>:*\n` : "",
@ -23,7 +23,7 @@ export default {
tag.answer,
].join("\n"),
allowed_mentions: {
parse: resolvedTarget ? ["users"] : [],
parse: resolvedTarget ? [AllowedMentionType.UserMentions] : [],
},
});
},

View file

@ -14,7 +14,7 @@ export function safeSlice<T extends string | Array<any>>(
export async function silently<T>(value: Promise<T>) {
try {
await value;
} catch {}
} catch { }
}
export async function moderateNick(member: GuildMember) {