diff --git a/src/commands/github/gh.ts b/src/commands/github/gh.ts index 3f0d1db6..8ed1acb7 100644 --- a/src/commands/github/gh.ts +++ b/src/commands/github/gh.ts @@ -5,12 +5,12 @@ import { SlashCommandBuilder, type ChatInputCommandInteraction, } from "discord.js"; -import { GitHubApiError } from "../../services/github/githubClient.js"; import { getIssue, listIssues, listPullRequests, } from "../../services/github/githubApi.js"; +import { getGitHubUserMessage } from "../../services/github/githubErrorMessage.js"; import { logger } from "../../utils/logger.js"; /** @@ -54,7 +54,7 @@ export const data = new SlashCommandBuilder() .addSubcommand((s) => s .setName("prs") - .setDescription("List pull requests") + .setDescription("List open pull requests") .addStringOption((o) => o.setName("owner").setDescription("Org/user").setRequired(true), ) @@ -107,7 +107,8 @@ export async function execute(interaction: ChatInputCommandInteraction): Promise `**Open issues for ${owner}/${repo}**`, "", ...issues.map( - (i) => `#${i.number} ${i.title} (by ${i.user?.login ?? "unknown"})`, + (i) => + `#${i.number} ${i.title} (by ${i.user?.login ?? "unknown"})\n${i.html_url}`, ), ].join("\n"), ); @@ -127,29 +128,26 @@ export async function execute(interaction: ChatInputCommandInteraction): Promise [ `**Open PRs for ${owner}/${repo}**`, "", - ...prs.map((p) => `#${p.number} ${p.title} (by ${p.user?.login ?? "unknown"})`), + ...prs.map( + (p) => + `#${p.number} ${p.title} (by ${p.user?.login ?? "unknown"})\n${p.html_url}`, + ), ].join("\n"), ); return; } - // Should be unreachable because subcommand is required, but keep it safe. await interaction.editReply("Unknown subcommand."); + return; } catch (err) { - if (err instanceof GitHubApiError) { - if (err.status === 404) { - await interaction.editReply("Not found. Check owner/repo and the number."); - return; - } - - logger.warn({ err, owner, repo, sub }, "[gh] GitHub API error"); - - await interaction.editReply(`GitHub error (${err.status}): ${err.message}`); + const msg = getGitHubUserMessage(err); + if (msg) { + await interaction.editReply(msg); return; } - logger.error({ err, owner, repo, sub }, "[gh] command failed"); - - await interaction.editReply("Something went wrong talking to GitHub."); + logger.warn({ err, owner, repo, sub }, "[gh] GitHub request failed"); + await interaction.editReply("GitHub request failed. Please try again in a bit."); + return; } } diff --git a/src/commands/github/pr.ts b/src/commands/github/pr.ts index d3aea2a0..9d1c60c5 100644 --- a/src/commands/github/pr.ts +++ b/src/commands/github/pr.ts @@ -5,8 +5,8 @@ import { SlashCommandBuilder, type ChatInputCommandInteraction, } from "discord.js"; -import { GitHubApiError } from "../../services/github/githubClient.js"; import { getPullRequest } from "../../services/github/githubApi.js"; +import { getGitHubUserMessage } from "../../services/github/githubErrorMessage.js"; import { logger } from "../../utils/logger.js"; /** @@ -15,7 +15,7 @@ import { logger } from "../../utils/logger.js"; */ export const data = new SlashCommandBuilder() .setName("pr") - .setDescription("GitHub pull request helpers") + .setDescription("Fetch a GitHub pull request by number") .addIntegerOption((o) => o.setName("number").setDescription("PR #").setRequired(true)) .addStringOption((o) => o.setName("owner").setDescription("Org/user").setRequired(true)) .addStringOption((o) => o.setName("repo").setDescription("Repo").setRequired(true)); @@ -31,30 +31,26 @@ export async function execute(interaction: ChatInputCommandInteraction): Promise const pr = await getPullRequest(owner, repo, number); const mergedLabel = pr.merged ? "merged" : "not merged"; + const body = [ `**${owner}/${repo} PR #${pr.number}**`, pr.title, `State: ${pr.state} (${mergedLabel})`, `Author: ${pr.user?.login ?? "unknown"}`, - `URL: ${pr.html_url}`, + pr.html_url, ].join("\n"); await interaction.editReply(body); + return; } catch (err) { - if (err instanceof GitHubApiError) { - if (err.status === 404) { - await interaction.editReply("PR not found. Check owner/repo and PR number."); - return; - } - - logger.warn({ err, owner, repo, number }, "[pr] GitHub API error"); - - await interaction.editReply(`GitHub API error (${err.status}): ${err.message}`); + const msg = getGitHubUserMessage(err); + if (msg) { + await interaction.editReply(msg); return; } - logger.error({ err, owner, repo, number }, "[pr] command failed"); - - await interaction.editReply("Something went wrong while talking to GitHub."); + logger.warn({ err, owner, repo, number }, "[pr] GitHub request failed"); + await interaction.editReply("GitHub request failed. Please try again in a bit."); + return; } } diff --git a/src/services/github/githubErrorMessage.ts b/src/services/github/githubErrorMessage.ts new file mode 100644 index 00000000..0d27afe2 --- /dev/null +++ b/src/services/github/githubErrorMessage.ts @@ -0,0 +1,17 @@ +import { GitHubApiError } from "./githubClient.js"; + +export function getGitHubUserMessage(err: unknown): string | null { + if (!(err instanceof GitHubApiError)) return null; + + switch (err.status) { + case 404: + return "I could not find that PR. Double check the number and repo."; + case 401: + case 403: + return "I cannot access that repo with the current GitHub token. Check token and repo permissions."; + case 429: + return "GitHub rate limited me. Try again in a bit."; + default: + return null; + } +}