mirror of
https://github.com/xHyroM/bun-discord-bot.git
synced 2024-11-22 14:41:05 +01:00
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:
parent
53f730b7aa
commit
b0438f26c0
20 changed files with 251 additions and 265 deletions
BIN
bun.lockb
BIN
bun.lockb
Binary file not shown.
|
@ -11,15 +11,15 @@
|
||||||
"bun-types": "^1.1.6"
|
"bun-types": "^1.1.6"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@lilybird/handlers": "^0.4.0",
|
"@lilybird/handlers": "^0.6.0-beta.9",
|
||||||
"@lilybird/jsx": "0.2.0",
|
"@lilybird/jsx": "0.3.0",
|
||||||
"@lilybird/transformers": "^0.2.0",
|
"@lilybird/transformers": "^0.4.1",
|
||||||
"@paperdave/logger": "^3.0.1",
|
"@paperdave/logger": "^3.0.1",
|
||||||
"@purplet/serialize": "^2.0.0",
|
"@purplet/serialize": "^2.0.0",
|
||||||
"@wolfram-alpha/wolfram-alpha-api": "^23.1004.144821-RELEASE",
|
"@wolfram-alpha/wolfram-alpha-api": "^23.1004.144821-RELEASE",
|
||||||
"algoliasearch": "^4.23.2",
|
"algoliasearch": "^4.23.2",
|
||||||
"bun-tracestrings": "github:oven-sh/bun.report",
|
"bun-tracestrings": "github:oven-sh/bun.report",
|
||||||
"gray-matter": "^4.0.3",
|
"gray-matter": "^4.0.3",
|
||||||
"lilybird": "^0.7.1-alpha.0"
|
"lilybird": "^0.7.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,9 +1,5 @@
|
||||||
import {
|
import { AllowedMentionType, ApplicationCommandOptionType } from "lilybird";
|
||||||
ApplicationCommand as JSXApplicationCommand,
|
import { $applicationCommand } from "@lilybird/handlers/advanced";
|
||||||
StringOption,
|
|
||||||
UserOption,
|
|
||||||
} from "@lilybird/jsx";
|
|
||||||
import { ApplicationCommand } from "@lilybird/handlers";
|
|
||||||
import algoliasearch from "algoliasearch";
|
import algoliasearch from "algoliasearch";
|
||||||
import { safeSlice } from "src/util.ts";
|
import { safeSlice } from "src/util.ts";
|
||||||
|
|
||||||
|
@ -14,19 +10,22 @@ const algoliaClient = algoliasearch(
|
||||||
);
|
);
|
||||||
const algoliaIndex = algoliaClient.initIndex("bun");
|
const algoliaIndex = algoliaClient.initIndex("bun");
|
||||||
|
|
||||||
export default {
|
$applicationCommand({
|
||||||
post: "GLOBAL",
|
name: "docs",
|
||||||
data: (
|
description: "Search at docs",
|
||||||
<JSXApplicationCommand name="docs" description="Search at docs">
|
options: [
|
||||||
<StringOption
|
{
|
||||||
name="query"
|
type: ApplicationCommandOptionType.STRING,
|
||||||
description="Select query"
|
name: "query",
|
||||||
required
|
description: "Select query",
|
||||||
autocomplete
|
required: true,
|
||||||
/>
|
autocomplete: true,
|
||||||
<UserOption name="target" description="User to mention" />
|
}, {
|
||||||
</JSXApplicationCommand>
|
type: ApplicationCommandOptionType.USER,
|
||||||
),
|
name: "target",
|
||||||
|
description: "User to mention"
|
||||||
|
}
|
||||||
|
],
|
||||||
autocomplete: async (interaction) => {
|
autocomplete: async (interaction) => {
|
||||||
const query = interaction.data.getFocused<string>().value;
|
const query = interaction.data.getFocused<string>().value;
|
||||||
const result = await algoliaIndex.search(query, {
|
const result = await algoliaIndex.search(query, {
|
||||||
|
@ -44,7 +43,7 @@ export default {
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
run: async (interaction) => {
|
handle: async (interaction) => {
|
||||||
await interaction.deferReply();
|
await interaction.deferReply();
|
||||||
|
|
||||||
const query = interaction.data.getString("query");
|
const query = interaction.data.getString("query");
|
||||||
|
@ -75,15 +74,14 @@ export default {
|
||||||
: "",
|
: "",
|
||||||
].join("\n");
|
].join("\n");
|
||||||
|
|
||||||
// @ts-expect-error allowed_mentions
|
|
||||||
await interaction.editReply({
|
await interaction.editReply({
|
||||||
content,
|
content,
|
||||||
allowed_mentions: {
|
allowed_mentions: {
|
||||||
parse: target ? ["users"] : [],
|
parse: target ? [AllowedMentionType.UserMentions] : [],
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
} satisfies ApplicationCommand;
|
});
|
||||||
|
|
||||||
function getHitName(hit: any) {
|
function getHitName(hit: any) {
|
||||||
const type = hit.hierarchy.lvl0 === "Documentation" ? "📖" : "🗺️";
|
const type = hit.hierarchy.lvl0 === "Documentation" ? "📖" : "🗺️";
|
|
@ -1,10 +1,6 @@
|
||||||
import {
|
|
||||||
ApplicationCommand as JSXApplicationCommand,
|
import { $applicationCommand } from "@lilybird/handlers/advanced";
|
||||||
BooleanOption,
|
import { ApplicationCommandOptionType } from "lilybird";
|
||||||
CommandOptions,
|
|
||||||
StringOption,
|
|
||||||
} from "@lilybird/jsx";
|
|
||||||
import { ApplicationCommand } from "@lilybird/handlers";
|
|
||||||
import { safeSlice, silently } from "../util.ts";
|
import { safeSlice, silently } from "../util.ts";
|
||||||
|
|
||||||
type State =
|
type State =
|
||||||
|
@ -37,56 +33,57 @@ interface Item {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export default {
|
$applicationCommand({
|
||||||
post: "GLOBAL",
|
name: "github",
|
||||||
data: (
|
description: "Query an issue, pull request or direct link to issue, pull request",
|
||||||
<JSXApplicationCommand
|
options: [
|
||||||
name="github"
|
{
|
||||||
description="Query an issue, pull request or direct link to issue, pull request"
|
type: ApplicationCommandOptionType.STRING,
|
||||||
>
|
name: "query",
|
||||||
<StringOption
|
description: "Issue/Pull request number or name",
|
||||||
name="query"
|
autocomplete: true,
|
||||||
description="Issue/Pull request number or name"
|
required: true,
|
||||||
autocomplete
|
max_length: 100
|
||||||
required
|
}, {
|
||||||
max_length={100}
|
type: ApplicationCommandOptionType.STRING,
|
||||||
/>
|
name: "state",
|
||||||
<StringOption name="state" description="Issue or Pull request state">
|
description: "Issue or Pull request state",
|
||||||
<CommandOptions name="🔴🟠 Open" value="open" />
|
choices: [
|
||||||
<CommandOptions
|
{ name: "🔴🟠 Open", value: "open" },
|
||||||
name="🟢 Closed as completed"
|
{ name: "🟢 Closed as completed", value: "closed_as_completed" },
|
||||||
value="closed_as_completed"
|
{ name: "⚪️ Closed as not planned", value: "closed_as_not_planned" },
|
||||||
/>
|
{ name: "⚫️ Closed", value: "closed" },
|
||||||
<CommandOptions
|
{ name: "🟣 Merged", value: "merged" },
|
||||||
name="⚪️ Closed as not planned"
|
{ name: "📝 Draft", value: "draft" },
|
||||||
value="closed_as_not_planned"
|
{ name: "🌍 All", value: "all" }
|
||||||
/>
|
]
|
||||||
<CommandOptions name="⚫️ Closed" value="closed" />
|
}, {
|
||||||
<CommandOptions name="🟣 Merged" value="merged" />
|
type: ApplicationCommandOptionType.STRING,
|
||||||
<CommandOptions name="📝 Draft" value="draft" />
|
name: "type",
|
||||||
<CommandOptions name="🌍 All" value="all" />
|
description: "Issue/Pull request number or name",
|
||||||
</StringOption>
|
choices: [
|
||||||
<StringOption name="type" description="Issue/Pull request number or name">
|
{ name: "🐛 Issues", value: "issues" },
|
||||||
<CommandOptions name="🐛 Issues" value="issues" />
|
{ name: "🔨 Pull Requests", value: "pull_requests" },
|
||||||
<CommandOptions name="🔨 Pull Requests" value="pull_requests" />
|
{ name: "🌍 Both", value: "both" }
|
||||||
<CommandOptions name="🌍 Both" value="both" />
|
],
|
||||||
</StringOption>
|
},
|
||||||
<BooleanOption name="hide" description="Show this message only for you" />
|
{
|
||||||
</JSXApplicationCommand>
|
type: ApplicationCommandOptionType.BOOLEAN,
|
||||||
),
|
name: "hide",
|
||||||
run: async (interaction) => {
|
description: "Show this message only for you"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
handle: async (interaction) => {
|
||||||
const hide = interaction.data.getBoolean("hide") ?? false;
|
const hide = interaction.data.getBoolean("hide") ?? false;
|
||||||
|
|
||||||
await interaction.deferReply(hide);
|
await interaction.deferReply(hide);
|
||||||
|
|
||||||
const query = interaction.data.getString("query", true);
|
const query = interaction.data.getString("query", true);
|
||||||
const state: State =
|
const state: State = (interaction.data.getString("state") as State) || "all";
|
||||||
(interaction.data.getString("state") as State) || "all";
|
|
||||||
const type: Type = (interaction.data.getString("type") as Type) || "both";
|
const type: Type = (interaction.data.getString("type") as Type) || "both";
|
||||||
|
|
||||||
const result = (await search(query, state, type))[0];
|
const result = (await search(query, state, type))[0];
|
||||||
if (!result) {
|
if (!result) {
|
||||||
// @ts-expect-error allowed_mentions
|
|
||||||
interaction.editReply({
|
interaction.editReply({
|
||||||
content: `❌ Couldn't find issue or pull request \`${query}\``,
|
content: `❌ Couldn't find issue or pull request \`${query}\``,
|
||||||
allowed_mentions: {
|
allowed_mentions: {
|
||||||
|
@ -96,13 +93,10 @@ export default {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// @ts-expect-error allowed_mentions
|
|
||||||
interaction.editReply({
|
interaction.editReply({
|
||||||
content: [
|
content: [
|
||||||
`${result.emoji.type} ${result.emoji.state} [#${
|
`${result.emoji.type} ${result.emoji.state} [#${result.number
|
||||||
result.number
|
} in oven-sh/bun](<${result.html_url}>) by [${result.user.login}](<${result.user.html_url
|
||||||
} in oven-sh/bun](<${result.html_url}>) by [${result.user.login}](<${
|
|
||||||
result.user.html_url
|
|
||||||
}>) ${stateToText(result)} ${stateToTimestamp(result)}`,
|
}>) ${stateToText(result)} ${stateToTimestamp(result)}`,
|
||||||
result.title,
|
result.title,
|
||||||
].join("\n"),
|
].join("\n"),
|
||||||
|
@ -111,7 +105,6 @@ export default {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
autocomplete: async (interaction) => {
|
autocomplete: async (interaction) => {
|
||||||
const query = interaction.data.getFocused<string>().value;
|
const query = interaction.data.getFocused<string>().value;
|
||||||
const state: State =
|
const state: State =
|
||||||
|
@ -132,7 +125,7 @@ export default {
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
} satisfies ApplicationCommand;
|
});
|
||||||
|
|
||||||
function stateToText(item: Item) {
|
function stateToText(item: Item) {
|
||||||
switch (item.emoji.state) {
|
switch (item.emoji.state) {
|
|
@ -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/autocomplete/mdnAutoComplete.ts#L23-L47 thanks
|
||||||
// https://github.com/discordjs/discord-utils-bot/blob/main/src/functions/mdn.ts#L59C1-L78C3 thanks
|
// https://github.com/discordjs/discord-utils-bot/blob/main/src/functions/mdn.ts#L59C1-L78C3 thanks
|
||||||
|
import { ApplicationCommandOptionType, AllowedMentionType } from "lilybird";
|
||||||
import {
|
import { $applicationCommand } from "@lilybird/handlers/advanced";
|
||||||
BooleanOption,
|
|
||||||
ApplicationCommand as JSXApplicationCommand,
|
|
||||||
StringOption,
|
|
||||||
UserOption,
|
|
||||||
} from "@lilybird/jsx";
|
|
||||||
import { ApplicationCommand } from "@lilybird/handlers";
|
|
||||||
import { MDN_API, MDN_DISCORD_EMOJI } from "src/constants.ts";
|
import { MDN_API, MDN_DISCORD_EMOJI } from "src/constants.ts";
|
||||||
import { safeSlice, silently } from "src/util.ts";
|
import { safeSlice, silently } from "src/util.ts";
|
||||||
|
|
||||||
|
@ -43,25 +37,29 @@ const MDN_DATA = (await (
|
||||||
|
|
||||||
const cache = new Map<string, Document>();
|
const cache = new Map<string, Document>();
|
||||||
|
|
||||||
export default {
|
$applicationCommand({
|
||||||
post: "GLOBAL",
|
|
||||||
data: (
|
name: "mdn",
|
||||||
<JSXApplicationCommand
|
description: "Search the Mozilla Developer Network documentation",
|
||||||
name="mdn"
|
options: [
|
||||||
description="Search the Mozilla Developer Network documentation"
|
{
|
||||||
>
|
type: ApplicationCommandOptionType.STRING,
|
||||||
<StringOption
|
name: "query",
|
||||||
name="query"
|
description: "Class or method to search for",
|
||||||
description="Class or method to search for"
|
required: true,
|
||||||
required
|
autocomplete: true,
|
||||||
autocomplete
|
max_length: 100
|
||||||
max_length={100}
|
}, {
|
||||||
/>
|
type: ApplicationCommandOptionType.USER,
|
||||||
<UserOption name="target" description="User to mention" />
|
name: "target",
|
||||||
<BooleanOption name="hide" description="Show this message only for you" />
|
description: "User to mention"
|
||||||
</JSXApplicationCommand>
|
}, {
|
||||||
),
|
type: ApplicationCommandOptionType.BOOLEAN,
|
||||||
run: async (interaction) => {
|
name: "hide",
|
||||||
|
description: "Show this message only for you"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
handle: async (interaction) => {
|
||||||
const hide = interaction.data.getBoolean("hide") ?? false;
|
const hide = interaction.data.getBoolean("hide") ?? false;
|
||||||
|
|
||||||
await interaction.deferReply(hide);
|
await interaction.deferReply(hide);
|
||||||
|
@ -101,14 +99,13 @@ export default {
|
||||||
intro,
|
intro,
|
||||||
];
|
];
|
||||||
|
|
||||||
// @ts-expect-error allowed_mentions
|
|
||||||
await interaction.editReply({
|
await interaction.editReply({
|
||||||
content: [
|
content: [
|
||||||
target ? `*Suggestion for <@${target}>:*\n` : "",
|
target ? `*Suggestion for <@${target}>:*\n` : "",
|
||||||
parts.join("\n"),
|
parts.join("\n"),
|
||||||
].join("\n"),
|
].join("\n"),
|
||||||
allowed_mentions: {
|
allowed_mentions: {
|
||||||
parse: target ? ["users"] : [],
|
parse: target ? [AllowedMentionType.UserMentions] : [],
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
@ -151,7 +148,7 @@ export default {
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
} satisfies ApplicationCommand;
|
});
|
||||||
|
|
||||||
function escape(text: string) {
|
function escape(text: string) {
|
||||||
return text.replaceAll("||", "|\u200B|").replaceAll("*", "\\*");
|
return text.replaceAll("||", "|\u200B|").replaceAll("*", "\\*");
|
|
@ -1,17 +1,42 @@
|
||||||
import {
|
import { $applicationCommand } from "@lilybird/handlers/advanced";
|
||||||
ActionRow,
|
|
||||||
Button,
|
|
||||||
ApplicationCommand as JSXApplicationCommand,
|
|
||||||
} from "@lilybird/jsx";
|
|
||||||
import { ApplicationCommand } from "@lilybird/handlers";
|
|
||||||
import { serializers as S } from "@purplet/serialize";
|
import { serializers as S } from "@purplet/serialize";
|
||||||
import { possibleClosedForm } from "../util.ts";
|
import { ActionRow, Button } from "@lilybird/jsx";
|
||||||
import { ButtonStyle } from "lilybird";
|
import { possibleClosedForm, silently } from "../util.ts";
|
||||||
|
import { ButtonStyle, ComponentType } from "lilybird";
|
||||||
|
|
||||||
export default {
|
$applicationCommand({
|
||||||
post: "GLOBAL",
|
name: "ping",
|
||||||
data: <JSXApplicationCommand name="ping" description="pong" />,
|
description: "pong",
|
||||||
run: async (interaction) => {
|
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();
|
await interaction.deferReply();
|
||||||
|
|
||||||
const { ws, rest } = await interaction.client.ping();
|
const { ws, rest } = await interaction.client.ping();
|
||||||
|
@ -46,4 +71,4 @@ export default {
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
} satisfies ApplicationCommand;
|
});
|
||||||
|
|
|
@ -1,32 +1,32 @@
|
||||||
import {
|
import { AllowedMentionType, ApplicationCommandOptionType } from "lilybird";
|
||||||
ApplicationCommand as JSXApplicationCommand,
|
import { $applicationCommand } from "@lilybird/handlers/advanced";
|
||||||
StringOption,
|
|
||||||
UserOption,
|
|
||||||
} from "@lilybird/jsx";
|
|
||||||
import { getTags, searchTag } from "../loaders/tags.ts";
|
import { getTags, searchTag } from "../loaders/tags.ts";
|
||||||
import { ApplicationCommand } from "@lilybird/handlers";
|
|
||||||
|
|
||||||
export default {
|
$applicationCommand({
|
||||||
post: "GLOBAL",
|
name: "tag",
|
||||||
data: (
|
description: "Get tag",
|
||||||
<JSXApplicationCommand name="tag" description="Get tag">
|
options: [
|
||||||
<StringOption
|
{
|
||||||
name="query"
|
|
||||||
description="Select query"
|
type: ApplicationCommandOptionType.STRING,
|
||||||
required
|
name: "query",
|
||||||
autocomplete
|
description: "Select query",
|
||||||
/>
|
required: true,
|
||||||
<UserOption name="target" description="User to mention" />
|
autocomplete: true
|
||||||
</JSXApplicationCommand>
|
},
|
||||||
),
|
{
|
||||||
run: async (interaction) => {
|
type: ApplicationCommandOptionType.USER,
|
||||||
|
name: "target",
|
||||||
|
description: "User to mention"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
handle: async (interaction) => {
|
||||||
if (!interaction.inGuild()) return;
|
if (!interaction.inGuild()) return;
|
||||||
const query = interaction.data.getString("query", true);
|
const query = interaction.data.getString("query", true);
|
||||||
const target = interaction.data.getUser("target");
|
const target = interaction.data.getUser("target");
|
||||||
|
|
||||||
const tag = searchTag(interaction.channel, query, false);
|
const tag = searchTag(interaction.channel, query, false);
|
||||||
if (!tag) {
|
if (!tag) {
|
||||||
// @ts-expect-error allowed_mentions
|
|
||||||
return interaction.reply({
|
return interaction.reply({
|
||||||
content: `\`❌\` Could not find a tag \`${query}\``,
|
content: `\`❌\` Could not find a tag \`${query}\``,
|
||||||
ephemeral: true,
|
ephemeral: true,
|
||||||
|
@ -36,7 +36,6 @@ export default {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// @ts-expect-error allowed_mentions
|
|
||||||
await interaction.reply({
|
await interaction.reply({
|
||||||
content: [
|
content: [
|
||||||
target ? `*Suggestion for <@${target}>:*\n` : "",
|
target ? `*Suggestion for <@${target}>:*\n` : "",
|
||||||
|
@ -44,7 +43,7 @@ export default {
|
||||||
tag.answer,
|
tag.answer,
|
||||||
].join("\n"),
|
].join("\n"),
|
||||||
allowed_mentions: {
|
allowed_mentions: {
|
||||||
parse: target ? ["users"] : [],
|
parse: target ? [AllowedMentionType.UserMentions] : [],
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
@ -60,4 +59,4 @@ export default {
|
||||||
|
|
||||||
return await interaction.showChoices(getTags(interaction.channel, 25));
|
return await interaction.showChoices(getTags(interaction.channel, 25));
|
||||||
},
|
},
|
||||||
} satisfies ApplicationCommand;
|
});
|
|
@ -1,3 +1,5 @@
|
||||||
|
import { $applicationCommand } from "@lilybird/handlers/advanced";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
COMMIT_HASH,
|
COMMIT_HASH,
|
||||||
PRODUCTION,
|
PRODUCTION,
|
||||||
|
@ -5,17 +7,14 @@ import {
|
||||||
LILYBIRD_HANDLERS_VERSION,
|
LILYBIRD_HANDLERS_VERSION,
|
||||||
LILYBIRD_JSX_VERSION,
|
LILYBIRD_JSX_VERSION,
|
||||||
} from "../constants.ts";
|
} from "../constants.ts";
|
||||||
import { ApplicationCommand as JSXApplicationCommand } from "@lilybird/jsx";
|
|
||||||
import { ApplicationCommand } from "@lilybird/handlers";
|
|
||||||
|
|
||||||
export default {
|
$applicationCommand({
|
||||||
post: "GLOBAL",
|
name: "version",
|
||||||
data: <JSXApplicationCommand name="version" description="Show version" />,
|
description: "Show version",
|
||||||
run: (interaction) => {
|
handle: (interaction) => {
|
||||||
interaction.reply({
|
interaction.reply({
|
||||||
content: [
|
content: [
|
||||||
`[git-bun-discord-bot-${COMMIT_HASH}](<https://github.com/xHyroM/bun-discord-bot/tree/${COMMIT_HASH}>) ${
|
`[git-bun-discord-bot-${COMMIT_HASH}](<https://github.com/xHyroM/bun-discord-bot/tree/${COMMIT_HASH}>) ${!PRODUCTION ? "(dev)" : ""
|
||||||
!PRODUCTION ? "(dev)" : ""
|
|
||||||
}`,
|
}`,
|
||||||
`[Bun v${Bun.version} (${Bun.revision})](<https://github.com/oven-sh/bun/releases/tag/bun-v${Bun.version}>)`,
|
`[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,
|
ephemeral: true,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
} satisfies ApplicationCommand;
|
});
|
12
src/handler.ts
Normal file
12
src/handler.ts
Normal 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);
|
30
src/index.ts
30
src/index.ts
|
@ -1,21 +1,34 @@
|
||||||
import "./loaders/tags.ts";
|
import "./loaders/tags.ts";
|
||||||
|
|
||||||
|
import { SimpleTransformers, transformers, handler } from "./handler.ts";
|
||||||
|
import { ClientListeners, Intents, createClient } from "lilybird";
|
||||||
import { createHandler } from "@lilybird/handlers/simple";
|
import { createHandler } from "@lilybird/handlers/simple";
|
||||||
import { createClient, Intents } from "lilybird";
|
import { info } from "@paperdave/logger"
|
||||||
|
|
||||||
// Make sure bubu will not crash
|
// Make sure bubu will not crash
|
||||||
process.on("unhandledRejection", console.error);
|
process.on("unhandledRejection", console.error);
|
||||||
process.on("uncaughtException", 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,
|
prefix: process.env.MESSAGE_PREFIX,
|
||||||
dirs: {
|
dirs: {
|
||||||
messageCommands: `${import.meta.dir}/message-commands`,
|
messageCommands: `${import.meta.dir}/message-commands`
|
||||||
slashCommands: `${import.meta.dir}/commands`,
|
|
||||||
listeners: `${import.meta.dir}/listeners`,
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
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({
|
await createClient({
|
||||||
token: process.env.DISCORD_BOT_TOKEN,
|
token: process.env.DISCORD_BOT_TOKEN,
|
||||||
intents: [
|
intents: [
|
||||||
|
@ -24,5 +37,10 @@ await createClient({
|
||||||
Intents.MESSAGE_CONTENT,
|
Intents.MESSAGE_CONTENT,
|
||||||
Intents.GUILD_MEMBERS,
|
Intents.GUILD_MEMBERS,
|
||||||
],
|
],
|
||||||
...listeners,
|
setup: async (client) => {
|
||||||
|
info(`Logged in as ${client.user.username} (${client.user.id})`);
|
||||||
|
await handler.loadGlobalCommands(client);
|
||||||
|
},
|
||||||
|
transformers,
|
||||||
|
listeners
|
||||||
});
|
});
|
||||||
|
|
|
@ -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">;
|
|
|
@ -1,9 +1,9 @@
|
||||||
|
import { $listener } from "../handler.ts";
|
||||||
import { moderateNick } from "../util.ts";
|
import { moderateNick } from "../util.ts";
|
||||||
import { Event } from "@lilybird/handlers";
|
|
||||||
|
|
||||||
export default {
|
$listener({
|
||||||
event: "guildMemberAdd",
|
event: "guildMemberAdd",
|
||||||
run: (member) => {
|
handle: (member) => {
|
||||||
moderateNick(member);
|
moderateNick(member);
|
||||||
},
|
},
|
||||||
} satisfies Event<"guildMemberAdd">;
|
});
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
|
import { $listener } from "../handler.ts";
|
||||||
import { moderateNick } from "../util.ts";
|
import { moderateNick } from "../util.ts";
|
||||||
import { Event } from "@lilybird/handlers";
|
|
||||||
|
|
||||||
export default {
|
$listener({
|
||||||
event: "guildMemberUpdate",
|
event: "guildMemberUpdate",
|
||||||
run: (member) => {
|
handle: (member) => {
|
||||||
moderateNick(member);
|
moderateNick(member);
|
||||||
},
|
},
|
||||||
} satisfies Event<"guildMemberUpdate">;
|
});
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
import { ButtonStyle } from "lilybird";
|
|
||||||
import { Message } from "@lilybird/transformers";
|
|
||||||
import { ActionRow, Button } from "@lilybird/jsx";
|
import { ActionRow, Button } from "@lilybird/jsx";
|
||||||
|
import { Message } from "@lilybird/transformers";
|
||||||
import { extname, basename } from "node:path";
|
import { extname, basename } from "node:path";
|
||||||
import { Event } from "@lilybird/handlers";
|
import { $listener } from "../handler.ts";
|
||||||
|
import { ButtonStyle } from "lilybird";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
getBunReportDetailsInMarkdown,
|
getBunReportDetailsInMarkdown,
|
||||||
getRandomBunEmoji,
|
getRandomBunEmoji,
|
||||||
|
@ -12,18 +13,16 @@ import {
|
||||||
|
|
||||||
const GITHUB_LINE_URL_REGEX =
|
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;
|
/(?: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;
|
const BUN_REPORT_URL_REGEX = /(https:\/\/bun\.report\/\d+\.\d+(\.\d+)?\/\S+)/g;
|
||||||
|
|
||||||
export default {
|
$listener({
|
||||||
event: "messageCreate",
|
event: "messageCreate",
|
||||||
run: async (message) => {
|
handle: (message) => {
|
||||||
if (handleBunOnlyChannel(message)) return;
|
if (handleBunOnlyChannel(message)) return;
|
||||||
if (!message.content?.toLowerCase().startsWith(process.env.MESSAGE_PREFIX))
|
if (!message.content?.toLowerCase().startsWith(process.env.MESSAGE_PREFIX))
|
||||||
return handleOthers(message);
|
return handleOthers(message);
|
||||||
},
|
},
|
||||||
} satisfies Event<"messageCreate">;
|
});
|
||||||
|
|
||||||
function handleOthers(message: Message): void {
|
function handleOthers(message: Message): void {
|
||||||
handleGithubLink(message);
|
handleGithubLink(message);
|
||||||
|
@ -92,10 +91,8 @@ async function handleGithubLink(message: Message): Promise<void> {
|
||||||
|
|
||||||
if (extension === "zig") extension = "rs";
|
if (extension === "zig") extension = "rs";
|
||||||
|
|
||||||
// @ts-expect-error allowed_mentions
|
|
||||||
message.reply({
|
message.reply({
|
||||||
content: `***${basename(path)}*** — *(L${firstLineNumber + 1}${
|
content: `***${basename(path)}*** — *(L${firstLineNumber + 1}${secondLineNumber ? `-L${secondLineNumber}` : ""
|
||||||
secondLineNumber ? `-L${secondLineNumber}` : ""
|
|
||||||
})*\n\`\`\`${extension}\n${safeSlice(
|
})*\n\`\`\`${extension}\n${safeSlice(
|
||||||
text,
|
text,
|
||||||
2000 - 6 - extension.length
|
2000 - 6 - extension.length
|
||||||
|
@ -104,8 +101,7 @@ async function handleGithubLink(message: Message): Promise<void> {
|
||||||
<ActionRow>
|
<ActionRow>
|
||||||
<Button
|
<Button
|
||||||
style={ButtonStyle.Link}
|
style={ButtonStyle.Link}
|
||||||
url={`https://github.com/${repo}/blob/${path}#L${
|
url={`https://github.com/${repo}/blob/${path}#L${firstLineNumber + 1
|
||||||
firstLineNumber + 1
|
|
||||||
}${secondLineNumber ? `-L${secondLineNumber}` : ""}`}
|
}${secondLineNumber ? `-L${secondLineNumber}` : ""}`}
|
||||||
label={repo}
|
label={repo}
|
||||||
/>
|
/>
|
||||||
|
@ -126,7 +122,6 @@ async function handleBunReportLink(message: Message): Promise<void> {
|
||||||
const data = await getBunReportDetailsInMarkdown(match[0]);
|
const data = await getBunReportDetailsInMarkdown(match[0]);
|
||||||
if (!data) return;
|
if (!data) return;
|
||||||
|
|
||||||
// @ts-expect-error allowed_mentions
|
|
||||||
message.reply({
|
message.reply({
|
||||||
content: data,
|
content: data,
|
||||||
allowed_mentions: {
|
allowed_mentions: {
|
||||||
|
|
|
@ -1,13 +1,14 @@
|
||||||
import { Event } from "@lilybird/handlers";
|
|
||||||
import { PartialMessage } from "@lilybird/transformers";
|
import { PartialMessage } from "@lilybird/transformers";
|
||||||
import { isBunOnlyLikeMessage } from "src/util.ts";
|
import { isBunOnlyLikeMessage } from "src/util.ts";
|
||||||
|
import { $listener } from "../handler.ts";
|
||||||
|
|
||||||
export default {
|
|
||||||
|
$listener({
|
||||||
event: "messageUpdate",
|
event: "messageUpdate",
|
||||||
run: async (message) => {
|
handle: (message) => {
|
||||||
if (handleBunOnlyChannel(message)) return;
|
if (handleBunOnlyChannel(message)) return;
|
||||||
},
|
},
|
||||||
} satisfies Event<"messageUpdate">;
|
});
|
||||||
|
|
||||||
function handleBunOnlyChannel(message: PartialMessage): boolean {
|
function handleBunOnlyChannel(message: PartialMessage): boolean {
|
||||||
if (message.channelId !== process.env.BUN_ONLY_CHANNEL_ID) return false;
|
if (message.channelId !== process.env.BUN_ONLY_CHANNEL_ID) return false;
|
||||||
|
|
|
@ -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">;
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { ApplicationCommandOptionChoiceStructure } from "lilybird";
|
|
||||||
import { PartialChannel, GuildTextChannel } from "@lilybird/transformers";
|
import { PartialChannel, GuildTextChannel } from "@lilybird/transformers";
|
||||||
|
import { ApplicationCommand } from "lilybird";
|
||||||
import { Tag } from "../structs/Tag.ts";
|
import { Tag } from "../structs/Tag.ts";
|
||||||
import { readFileSync } from "node:fs";
|
import { readFileSync } from "node:fs";
|
||||||
import { safeSlice } from "../util.ts";
|
import { safeSlice } from "../util.ts";
|
||||||
|
@ -49,9 +49,9 @@ function getAvailableTags(channel: PartialGuildTextChannel) {
|
||||||
export function getTags(
|
export function getTags(
|
||||||
channel: PartialGuildTextChannel,
|
channel: PartialGuildTextChannel,
|
||||||
length: number
|
length: number
|
||||||
): Array<ApplicationCommandOptionChoiceStructure> {
|
): Array<ApplicationCommand.Option.ChoiceStructure> {
|
||||||
const availableTags = getAvailableTags(channel);
|
const availableTags = getAvailableTags(channel);
|
||||||
return safeSlice<Array<ApplicationCommandOptionChoiceStructure>>(
|
return safeSlice<Array<ApplicationCommand.Option.ChoiceStructure>>(
|
||||||
availableTags.map((tag) => ({
|
availableTags.map((tag) => ({
|
||||||
name: `🚀 ${tag.question}`,
|
name: `🚀 ${tag.question}`,
|
||||||
value: tag.question,
|
value: tag.question,
|
||||||
|
@ -64,7 +64,7 @@ export function searchTag<T extends boolean>(
|
||||||
channel: PartialGuildTextChannel,
|
channel: PartialGuildTextChannel,
|
||||||
providedQuery: string,
|
providedQuery: string,
|
||||||
multiple?: T
|
multiple?: T
|
||||||
): T extends true ? Array<ApplicationCommandOptionChoiceStructure> : Tag {
|
): T extends true ? Array<ApplicationCommand.Option.ChoiceStructure> : Tag {
|
||||||
const availableTags = getAvailableTags(channel);
|
const availableTags = getAvailableTags(channel);
|
||||||
const query = providedQuery?.toLowerCase()?.replace(/-/g, " ");
|
const query = providedQuery?.toLowerCase()?.replace(/-/g, " ");
|
||||||
|
|
||||||
|
@ -84,14 +84,14 @@ export function searchTag<T extends boolean>(
|
||||||
|
|
||||||
const tag = exactKeyword ?? questionMatch ?? keywordMatch ?? answerMatch;
|
const tag = exactKeyword ?? questionMatch ?? keywordMatch ?? answerMatch;
|
||||||
return tag as T extends true
|
return tag as T extends true
|
||||||
? Array<ApplicationCommandOptionChoiceStructure>
|
? Array<ApplicationCommand.Option.ChoiceStructure>
|
||||||
: Tag;
|
: Tag;
|
||||||
}
|
}
|
||||||
|
|
||||||
const exactKeywords: Array<ApplicationCommandOptionChoiceStructure> = [];
|
const exactKeywords: Array<ApplicationCommand.Option.ChoiceStructure> = [];
|
||||||
const keywordMatches: Array<ApplicationCommandOptionChoiceStructure> = [];
|
const keywordMatches: Array<ApplicationCommand.Option.ChoiceStructure> = [];
|
||||||
const questionMatches: Array<ApplicationCommandOptionChoiceStructure> = [];
|
const questionMatches: Array<ApplicationCommand.Option.ChoiceStructure> = [];
|
||||||
const answerMatches: Array<ApplicationCommandOptionChoiceStructure> = [];
|
const answerMatches: Array<ApplicationCommand.Option.ChoiceStructure> = [];
|
||||||
|
|
||||||
for (const tag of availableTags) {
|
for (const tag of availableTags) {
|
||||||
const exactKeyword = tag.keywords.find((t) => t.toLowerCase() === query);
|
const exactKeyword = tag.keywords.find((t) => t.toLowerCase() === query);
|
||||||
|
@ -131,6 +131,6 @@ export function searchTag<T extends boolean>(
|
||||||
...answerMatches,
|
...answerMatches,
|
||||||
];
|
];
|
||||||
return tags as T extends true
|
return tags as T extends true
|
||||||
? Array<ApplicationCommandOptionChoiceStructure>
|
? Array<ApplicationCommand.Option.ChoiceStructure>
|
||||||
: Tag;
|
: Tag;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
|
import { MessageCommand } from "@lilybird/handlers/simple";
|
||||||
import { serializers as S } from "@purplet/serialize";
|
import { serializers as S } from "@purplet/serialize";
|
||||||
import { MessageCommand } from "@lilybird/handlers";
|
|
||||||
import { ActionRow, Button } from "@lilybird/jsx";
|
import { ActionRow, Button } from "@lilybird/jsx";
|
||||||
import { possibleClosedForm } from "../util.ts";
|
import { possibleClosedForm } from "../util.ts";
|
||||||
import { ButtonStyle } from "lilybird";
|
import { ButtonStyle } from "lilybird";
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import { MessageCommand } from "@lilybird/handlers";
|
import { MessageCommand } from "@lilybird/handlers/simple";
|
||||||
import { searchTag } from "../loaders/tags.ts";
|
import { searchTag } from "../loaders/tags.ts";
|
||||||
|
import { AllowedMentionType } from "lilybird";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "tag",
|
name: "tag",
|
||||||
|
@ -15,7 +16,6 @@ export default {
|
||||||
const tag = searchTag(await message.fetchChannel(), keyword, false);
|
const tag = searchTag(await message.fetchChannel(), keyword, false);
|
||||||
if (!keyword || !tag) return; // just ignore
|
if (!keyword || !tag) return; // just ignore
|
||||||
|
|
||||||
// @ts-expect-error allowed_mentions
|
|
||||||
message.reply({
|
message.reply({
|
||||||
content: [
|
content: [
|
||||||
resolvedTarget ? `*Suggestion for <@${resolvedTarget.id}>:*\n` : "",
|
resolvedTarget ? `*Suggestion for <@${resolvedTarget.id}>:*\n` : "",
|
||||||
|
@ -23,7 +23,7 @@ export default {
|
||||||
tag.answer,
|
tag.answer,
|
||||||
].join("\n"),
|
].join("\n"),
|
||||||
allowed_mentions: {
|
allowed_mentions: {
|
||||||
parse: resolvedTarget ? ["users"] : [],
|
parse: resolvedTarget ? [AllowedMentionType.UserMentions] : [],
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
Loading…
Reference in a new issue