mirror of
https://github.com/xHyroM/bun-discord-bot.git
synced 2024-11-14 02:48:07 +01:00
feat: support search syntax in github command
This commit is contained in:
parent
7710f9c6bd
commit
facd7cf4dc
6 changed files with 64 additions and 34 deletions
BIN
bun.lockb
BIN
bun.lockb
Binary file not shown.
|
@ -12,7 +12,6 @@
|
||||||
"@discordjs/collection": "^0.7.0",
|
"@discordjs/collection": "^0.7.0",
|
||||||
"discord-api-types": "^0.36.1",
|
"discord-api-types": "^0.36.1",
|
||||||
"hono": "^1.6.4",
|
"hono": "^1.6.4",
|
||||||
"ms": "^2.1.3",
|
|
||||||
"tweetnacl": "^1.0.3"
|
"tweetnacl": "^1.0.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -8,9 +8,14 @@ import { githubIssuesAndPullRequests } from '../utils/regexes';
|
||||||
import isNumeric from '../utils/isNumeric';
|
import isNumeric from '../utils/isNumeric';
|
||||||
import Collection from '@discordjs/collection';
|
import Collection from '@discordjs/collection';
|
||||||
import formatStatus from '../utils/formatStatus';
|
import formatStatus from '../utils/formatStatus';
|
||||||
import ms from 'ms';
|
import { CommandContext } from '../structures/contexts/CommandContext';
|
||||||
|
|
||||||
const cooldowns: Collection<string, number> = new Collection();
|
const cooldowns: Collection<string, number> = new Collection();
|
||||||
|
const invalidIssue = (ctx: CommandContext, query: string) => {
|
||||||
|
return ctx.editResponse(
|
||||||
|
`\`❌\` Invalid issue or pull request \`${query}\`. You can check [github search syntax](https://docs.github.com/en/search-github/searching-on-github/searching-issues-and-pull-requests)`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
new Command({
|
new Command({
|
||||||
name: 'github',
|
name: 'github',
|
||||||
|
@ -18,7 +23,7 @@ new Command({
|
||||||
options: [
|
options: [
|
||||||
{
|
{
|
||||||
name: 'query',
|
name: 'query',
|
||||||
description: 'Issue, PR number or direct link to Github Issue or PR',
|
description: 'Issue numer/name, PR number/name or direct link to Github Issue or PR',
|
||||||
type: ApplicationCommandOptionType.String,
|
type: ApplicationCommandOptionType.String,
|
||||||
required: true
|
required: true
|
||||||
},
|
},
|
||||||
|
@ -40,13 +45,19 @@ new Command({
|
||||||
return ctx.respond({
|
return ctx.respond({
|
||||||
type: InteractionResponseType.ChannelMessageWithSource,
|
type: InteractionResponseType.ChannelMessageWithSource,
|
||||||
data: {
|
data: {
|
||||||
content: `⚠️ You are in cooldown, please wait ${ms(cooldowns.get(ctx.user.id) - Date.now())}.`,
|
content: `⚠️ You are in cooldown, please wait <t:${Math.floor(cooldowns.get(ctx.user.id) / 1000)}:R>.`,
|
||||||
flags: MessageFlags.Ephemeral,
|
flags: MessageFlags.Ephemeral,
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
ctx.command.runWithoutReturn(ctx)
|
||||||
|
return ctx.respond({
|
||||||
|
type: InteractionResponseType.DeferredChannelMessageWithSource
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
},
|
||||||
const query: string = (ctx.options[0] as APIApplicationCommandInteractionDataStringOption).value;
|
runWithoutReturn: async(ctx) => {
|
||||||
|
let query: string = (ctx.options[0] as APIApplicationCommandInteractionDataStringOption).value;
|
||||||
const repository: string = (ctx.options?.[1] as APIApplicationCommandInteractionDataStringOption)?.value || 'oven-sh/bun';
|
const repository: string = (ctx.options?.[1] as APIApplicationCommandInteractionDataStringOption)?.value || 'oven-sh/bun';
|
||||||
|
|
||||||
const repositorySplit = repository.split('/');
|
const repositorySplit = repository.split('/');
|
||||||
|
@ -56,18 +67,17 @@ new Command({
|
||||||
const isIssueOrPR = githubIssuesAndPullRequests(repositoryOwner, repositoryName).test(query);
|
const isIssueOrPR = githubIssuesAndPullRequests(repositoryOwner, repositoryName).test(query);
|
||||||
const isIssueOrPRNumber = isNumeric(query);
|
const isIssueOrPRNumber = isNumeric(query);
|
||||||
|
|
||||||
|
cooldowns.set(ctx.user.id, Date.now() + 30000);
|
||||||
if (!isIssueOrPR && !isIssueOrPRNumber) {
|
if (!isIssueOrPR && !isIssueOrPRNumber) {
|
||||||
return ctx.respond({
|
const res = await fetch(`https://api.github.com/search/issues?q=${encodeURIComponent(query)}${encodeURIComponent(' repo:oven-sh/bun')}`);
|
||||||
type: InteractionResponseType.ChannelMessageWithSource,
|
|
||||||
data: {
|
const data: any = await res.json();
|
||||||
content: `\`❌\` Invalid issue or pull request \`${query}\``,
|
if (data.message || data?.items?.length === 0) return invalidIssue(ctx, query);
|
||||||
flags: MessageFlags.Ephemeral,
|
|
||||||
}
|
query = data.items[0].number;
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const issueUrl = `https://api.github.com/repos/${repositoryOwner}/${repositoryName}/issues/${isIssueOrPR ? query.split('/issues/')[1] : query}`;
|
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, {
|
const res = await fetch(issueUrl, {
|
||||||
headers: {
|
headers: {
|
||||||
|
@ -78,17 +88,9 @@ new Command({
|
||||||
});
|
});
|
||||||
|
|
||||||
const data: any = await res.json();
|
const data: any = await res.json();
|
||||||
if (data.message) {
|
if (data.message) return invalidIssue(ctx, query);
|
||||||
return ctx.respond({
|
|
||||||
type: InteractionResponseType.ChannelMessageWithSource,
|
|
||||||
data: {
|
|
||||||
content: `\`❌\` Invalid issue or pull request \`${query}\``,
|
|
||||||
flags: MessageFlags.Ephemeral,
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return ctx.respond([
|
return ctx.editResponse([
|
||||||
`[#${data.number} ${repositoryOwner}/${repositoryName}](<${data.html_url}>) by [${data.user.login}](<${data.user.html_url}>) ${formatStatus(data)}`,
|
`[#${data.number} ${repositoryOwner}/${repositoryName}](<${data.html_url}>) by [${data.user.login}](<${data.user.html_url}>) ${formatStatus(data)}`,
|
||||||
data.title
|
data.title
|
||||||
].join('\n'));
|
].join('\n'));
|
||||||
|
|
|
@ -61,11 +61,11 @@ app.post('/interaction', bodyParse(), async(c) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (interaction.type === InteractionType.ApplicationCommand && interaction.data.type === ApplicationCommandType.ChatInput) {
|
if (interaction.type === InteractionType.ApplicationCommand && interaction.data.type === ApplicationCommandType.ChatInput) {
|
||||||
return await Commands.get(interaction.data.name).run(new CommandContext(
|
const commands = Commands.get(interaction.data.name);
|
||||||
|
return await commands.run(new CommandContext(
|
||||||
c,
|
c,
|
||||||
interaction.member,
|
commands,
|
||||||
interaction.data.options,
|
interaction
|
||||||
interaction.data.resolved
|
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,6 +16,7 @@ export interface CommandOptions {
|
||||||
defaultMemberPermissions?: string;
|
defaultMemberPermissions?: string;
|
||||||
options?: Option[] | OptionOptions[];
|
options?: Option[] | OptionOptions[];
|
||||||
run: (ctx: CommandContext) => Response | Promise<Response>;
|
run: (ctx: CommandContext) => Response | Promise<Response>;
|
||||||
|
runWithoutReturn?: (ctx: CommandContext) => any;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Command {
|
export class Command {
|
||||||
|
@ -27,6 +28,7 @@ export class Command {
|
||||||
public defaultMemberPermissions?: string;
|
public defaultMemberPermissions?: string;
|
||||||
public options: Option[] | OptionOptions[];
|
public options: Option[] | OptionOptions[];
|
||||||
public run: (ctx: CommandContext) => Response | Promise<Response>;
|
public run: (ctx: CommandContext) => Response | Promise<Response>;
|
||||||
|
public runWithoutReturn: (ctx: CommandContext) => any;
|
||||||
|
|
||||||
public constructor(options: CommandOptions) {
|
public constructor(options: CommandOptions) {
|
||||||
this.name = options.name;
|
this.name = options.name;
|
||||||
|
@ -43,6 +45,7 @@ export class Command {
|
||||||
else return new Option(option);
|
else return new Option(option);
|
||||||
});
|
});
|
||||||
this.run = options.run;
|
this.run = options.run;
|
||||||
|
this.runWithoutReturn = options.runWithoutReturn;
|
||||||
|
|
||||||
Commands.register(this);
|
Commands.register(this);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,19 +1,28 @@
|
||||||
import { APIApplicationCommandInteractionDataOption, APIChatInputApplicationCommandInteractionDataResolved, APIInteractionGuildMember, APIInteractionResponse, APIUser, InteractionResponseType } from 'discord-api-types/v10';
|
import { APIApplicationCommandInteraction, APIApplicationCommandInteractionDataOption, APIChatInputApplicationCommandInteraction, APIChatInputApplicationCommandInteractionDataResolved, APIInteractionGuildMember, APIInteractionResponse, APIInteractionResponseCallbackData, APIUser, ApplicationCommandType, InteractionResponseType, InteractionType, RouteBases, Routes } from 'discord-api-types/v10';
|
||||||
import { Context } from 'hono';
|
import { Context } from 'hono';
|
||||||
|
import { Command } from '../Command';
|
||||||
|
// @ts-expect-error Types :(
|
||||||
|
import config from '../../../files/config.toml';
|
||||||
|
|
||||||
export class CommandContext {
|
export class CommandContext {
|
||||||
public context: Context;
|
public context: Context;
|
||||||
|
public command?: Command;
|
||||||
|
public interaction?: APIChatInputApplicationCommandInteraction;
|
||||||
public user?: APIUser;
|
public user?: APIUser;
|
||||||
public member?: APIInteractionGuildMember;
|
public member?: APIInteractionGuildMember;
|
||||||
public options?: APIApplicationCommandInteractionDataOption[];
|
public options?: APIApplicationCommandInteractionDataOption[];
|
||||||
public resolved?: APIChatInputApplicationCommandInteractionDataResolved;
|
public resolved?: APIChatInputApplicationCommandInteractionDataResolved;
|
||||||
|
|
||||||
public constructor(c: Context, member?: APIInteractionGuildMember, options?: APIApplicationCommandInteractionDataOption[], resolved?: APIChatInputApplicationCommandInteractionDataResolved) {
|
public constructor(c: Context, command?: Command, interaction?: APIApplicationCommandInteraction) {
|
||||||
|
if (interaction.data.type != ApplicationCommandType.ChatInput) return;
|
||||||
|
|
||||||
this.context = c;
|
this.context = c;
|
||||||
this.user = member.user;
|
this.command = command;
|
||||||
this.member = member;
|
this.interaction = interaction as APIChatInputApplicationCommandInteraction;
|
||||||
this.options = options;
|
this.user = interaction.member.user;
|
||||||
this.resolved = resolved;
|
this.member = interaction.member;
|
||||||
|
this.options = interaction.data.options;
|
||||||
|
this.resolved = interaction.data.resolved;
|
||||||
}
|
}
|
||||||
|
|
||||||
public respond(response: APIInteractionResponse | string) {
|
public respond(response: APIInteractionResponse | string) {
|
||||||
|
@ -28,4 +37,21 @@ export class CommandContext {
|
||||||
|
|
||||||
return this.context.json(response);
|
return this.context.json(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async editResponse(response: APIInteractionResponseCallbackData | string) {
|
||||||
|
if (typeof response === 'string') {
|
||||||
|
response = {
|
||||||
|
content: response
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fetch(`${RouteBases.api}${Routes.webhookMessage(this.interaction.application_id, this.interaction.token)}`, {
|
||||||
|
method: 'PATCH',
|
||||||
|
headers: {
|
||||||
|
'Authorization': `Bot ${config.client.token}`,
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
body: JSON.stringify(response)
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
Loading…
Reference in a new issue