Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 15 additions & 17 deletions src/commands/github/gh.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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";

/**
Expand Down Expand Up @@ -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),
)
Expand Down Expand Up @@ -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"),
);
Expand All @@ -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;
}
}
26 changes: 11 additions & 15 deletions src/commands/github/pr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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";

/**
Expand All @@ -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));
Expand All @@ -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;
}
}
17 changes: 17 additions & 0 deletions src/services/github/githubErrorMessage.ts
Original file line number Diff line number Diff line change
@@ -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;
}
}