From d89b80c60d2e49e32ca17315c017d0709b00f583 Mon Sep 17 00:00:00 2001 From: xHyroM Date: Sun, 10 Jul 2022 21:41:00 +0200 Subject: [PATCH] feat: ping command, github command --- .gitignore | 1 + bun.lockb | Bin 2295 -> 2295 bytes files/config.example.toml | 8 ++- files/utilities.toml | 7 ++ package.json | 3 +- src/commands/github.ts | 81 ++++++++++++++++++++++ src/commands/{help.ts => ping.ts} | 11 ++- src/commands/tags.ts | 11 ++- src/index.ts | 2 + src/structures/Command.ts | 8 ++- src/structures/contexts/CommandContext.ts | 6 +- src/utils/isNumeric.ts | 1 + src/utils/regexes.ts | 2 + 13 files changed, 122 insertions(+), 19 deletions(-) create mode 100644 files/utilities.toml create mode 100644 src/commands/github.ts rename src/commands/{help.ts => ping.ts} (60%) create mode 100644 src/utils/isNumeric.ts create mode 100644 src/utils/regexes.ts diff --git a/.gitignore b/.gitignore index 9ff9480..3c51502 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ # Edit at https://www.toptal.com/developers/gitignore?templates=node files/config.toml +requests.rest ### Node ### # Logs diff --git a/bun.lockb b/bun.lockb index 5ef2cff3f47c44717f5cc2e659ed6e663e6313df..d3b7e524218542c622cdb29dbb97aa12fd0cbb40 100755 GIT binary patch delta 219 zcmew^_+4{URTe$rKC$(GJp%(H0|Ya<0Qr+;8BJKgj8GV(m(hd=Bmz_l z0_>9)GHJ7d6>t8_xP+-5WFAxwB%i>;(8J&ZQQyEI-}9M;fuSEHL1i%yR*<0#44V~L HuQLMxTQERE delta 219 zcmew^_+4 = new Collection(); + +new Command({ + name: 'github', + description: 'Query an issue, pull request or direct link to Github Issue or PR', + options: [ + { + name: 'query', + description: 'Issue, PR number or direct link to Github Issue or PR', + type: ApplicationCommandOptionType.String, + required: true + }, + { + name: 'repository', + description: 'Project repository (default oven-sh/bun)', + type: ApplicationCommandOptionType.String, + required: false, + choices: [ + ...utilities.github.repositories.map(repository => new Object({ + name: repository.split('/')[1], + value: repository + })) + ] + } + ], + run: async(ctx) => { + if (cooldowns.has(ctx.user.id) && cooldowns.get(ctx.user.id) < Date.now()) { + return ctx.respond('⚠️ You are in cooldown.'); + } + + const query: string = (ctx.options[0] as APIApplicationCommandInteractionDataStringOption).value; + const repository: string = (ctx.options?.[1] as APIApplicationCommandInteractionDataStringOption)?.value || 'oven-sh/bun'; + + const repositorySplit = repository.split('/'); + const repositoryOwner = repositorySplit[0]; + const repositoryName = repositorySplit[1]; + + const isIssueOrPR = githubIssuesAndPullRequests(repositoryOwner, repositoryName).test(query); + const isIssueOrPRNumber = isNumeric(query); + + if (!isIssueOrPR && !isIssueOrPRNumber) { + return ctx.respond({ + type: InteractionResponseType.ChannelMessageWithSource, + data: { + content: `\`❌\` Invalid issue or pull request \`${query}\``, + flags: MessageFlags.Ephemeral, + } + }); + } + + const issueUrl = `https://api.github.com/repos/${repositoryOwner}/${repositoryName}/issues/${isIssueOrPR ? query.split('/issues/')[1] : query}`; + cooldowns.set(ctx.user.id, Date.now() + 60000); + + const res = await fetch(issueUrl, { + headers: { + 'Content-Type': 'application/json', + 'User-Agent': 'bun-discord-bot', + 'Authorization': config.api.github_personal_access_token + } + }); + + const data: any = await res.json(); + + // TODO: finish (pull request, issue) + + return ctx.respond([ + `[#${data.number} ${repositoryOwner}/${repositoryName}](${data.html_url}) operation `, + data.title + ].join('\n')); + } +}) \ No newline at end of file diff --git a/src/commands/help.ts b/src/commands/ping.ts similarity index 60% rename from src/commands/help.ts rename to src/commands/ping.ts index 4877f2a..f4be607 100644 --- a/src/commands/help.ts +++ b/src/commands/ping.ts @@ -2,14 +2,13 @@ import { InteractionResponseType } from 'discord-api-types/v10'; import { Command } from '../structures/Command'; new Command({ - name: 'help', - description: 'Help command', - guildId: '924395690451423332', - run: (c) => { - return c.respond({ + name: 'ping', + description: 'pong', + run: (ctx) => { + return ctx.respond({ type: InteractionResponseType.ChannelMessageWithSource, data: { - content: 'hello' + content: 'Pong 🏓' } }) } diff --git a/src/commands/tags.ts b/src/commands/tags.ts index f049929..f15e478 100644 --- a/src/commands/tags.ts +++ b/src/commands/tags.ts @@ -5,7 +5,6 @@ import { findTags, getTag } from '../utils/tagsUtils'; new Command({ name: 'tags', description: 'Send a tag by name or alias', - guildId: '924395690451423332', options: [ { name: 'query', @@ -23,13 +22,13 @@ new Command({ required: false } ], - run: (c) => { - const query: APIApplicationCommandInteractionDataStringOption = c.options[0] as APIApplicationCommandInteractionDataStringOption; - const target = c?.resolved?.users?.[0]; + run: (ctx) => { + const query: APIApplicationCommandInteractionDataStringOption = ctx.options[0] as APIApplicationCommandInteractionDataStringOption; + const target = ctx?.resolved?.users?.[0]; const tag = getTag(query.value); if (!tag) - return c.respond({ + return ctx.respond({ type: InteractionResponseType.ChannelMessageWithSource, data: { content: `\`❌\` Could not find a tag \`${query.value}\``, @@ -37,7 +36,7 @@ new Command({ } }); - return c.respond([ + return ctx.respond([ target ? `*Tag suggestion for <@${target.id}>:*` : '', tag.content ].join('\n')); diff --git a/src/index.ts b/src/index.ts index b00c54f..cdd2ad3 100644 --- a/src/index.ts +++ b/src/index.ts @@ -21,6 +21,7 @@ try { } const app = new Hono(); +app.get('*', (c) => c.redirect('https://www.youtube.com/watch?v=FMhScnY0dME')); app.post('/interaction', bodyParse(), async(c) => { const signature = c.req.headers.get('X-Signature-Ed25519'); @@ -62,6 +63,7 @@ app.post('/interaction', bodyParse(), async(c) => { if (interaction.type === InteractionType.ApplicationCommand && interaction.data.type === ApplicationCommandType.ChatInput) { return Commands.get(interaction.data.name).run(new CommandContext( c, + interaction.user, interaction.data.options, interaction.data.resolved )); diff --git a/src/structures/Command.ts b/src/structures/Command.ts index a5b5007..ee4cd4c 100644 --- a/src/structures/Command.ts +++ b/src/structures/Command.ts @@ -1,5 +1,7 @@ // Taken from https://github.com/Garlic-Team/gcommands/blob/next/src/lib/structures/Command.ts +// @ts-expect-error Types :( +import config from '../../files/config.toml'; import { LocaleString } from 'discord-api-types/v10'; import { Commands } from '../managers/CommandManager'; import { CommandContext } from './contexts/CommandContext'; @@ -13,7 +15,7 @@ export interface CommandOptions { guildId?: string; defaultMemberPermissions?: string; options?: Option[] | OptionOptions[]; - run: (ctx: CommandContext) => Response; + run: (ctx: CommandContext) => Response | Promise; } export class Command { @@ -21,10 +23,10 @@ export class Command { public nameLocalizations?: Record; public description?: string; public descriptionLocalizations?: Record; - public guildId?: string; + public guildId?: string = config.client.guild_id; public defaultMemberPermissions?: string; public options: Option[] | OptionOptions[]; - public run: (ctx: CommandContext) => Response; + public run: (ctx: CommandContext) => Response | Promise; public constructor(options: CommandOptions) { this.name = options.name; diff --git a/src/structures/contexts/CommandContext.ts b/src/structures/contexts/CommandContext.ts index 4443803..808da5f 100644 --- a/src/structures/contexts/CommandContext.ts +++ b/src/structures/contexts/CommandContext.ts @@ -1,13 +1,15 @@ -import { APIApplicationCommandInteractionDataOption, APIChatInputApplicationCommandInteractionDataResolved, APIInteractionResponse, InteractionResponseType } from 'discord-api-types/v10'; +import { APIApplicationCommandInteractionDataOption, APIChatInputApplicationCommandInteractionDataResolved, APIInteractionResponse, APIUser, InteractionResponseType } from 'discord-api-types/v10'; import { Context } from 'hono'; export class CommandContext { public context: Context; + public user?: APIUser; public options?: APIApplicationCommandInteractionDataOption[]; public resolved?: APIChatInputApplicationCommandInteractionDataResolved; - public constructor(c: Context, options?: APIApplicationCommandInteractionDataOption[], resolved?: APIChatInputApplicationCommandInteractionDataResolved) { + public constructor(c: Context, user?: APIUser, options?: APIApplicationCommandInteractionDataOption[], resolved?: APIChatInputApplicationCommandInteractionDataResolved) { this.context = c; + this.user = user; this.options = options; this.resolved = resolved; } diff --git a/src/utils/isNumeric.ts b/src/utils/isNumeric.ts new file mode 100644 index 0000000..939a452 --- /dev/null +++ b/src/utils/isNumeric.ts @@ -0,0 +1 @@ +export default (value: string) => /^-?\d+$/.test(value); \ No newline at end of file diff --git a/src/utils/regexes.ts b/src/utils/regexes.ts new file mode 100644 index 0000000..39104d0 --- /dev/null +++ b/src/utils/regexes.ts @@ -0,0 +1,2 @@ +export const githubIssuesAndPullRequests = (owner: string, repository: string) => + new RegExp(`https?:\\\/\\\/github\\\.com\\\/${owner}\\\/${repository}\\\/(?:issues\\\/\\\d+|pull\\\/\d+)`, 'gm'); \ No newline at end of file