Skip to content

Commit cc5befc

Browse files
authored
refactor(gan): extract shared scaffolding from /gan and /gan2 slash commands (#57)
1 parent 50cbff2 commit cc5befc

3 files changed

Lines changed: 75 additions & 86 deletions

File tree

src/slash-commands/gan.command.ts

Lines changed: 13 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
11
import { SlashCommandBuilder } from "discord.js";
22

33
import type { SlashCommand } from "../models";
4-
import { GameInfoService } from "../services/game-info.service";
54
import { TemplateService } from "../services/template.service";
6-
import { fetchGanData } from "../utils/fetch-gan-data";
7-
import { logError } from "../utils/logger";
5+
import { runGanCommand } from "./shared/run-gan-command";
86

97
const ganSlashCommand: SlashCommand = {
108
data: new SlashCommandBuilder()
@@ -17,49 +15,20 @@ const ganSlashCommand: SlashCommand = {
1715
.setRequired(true),
1816
),
1917

20-
legacyName: "gan", // For migration mapping - using the most common alias
18+
legacyName: "gan", // For migration mapping - using the most common alias.
2119

2220
async execute(interaction, _client) {
23-
await interaction.deferReply();
24-
25-
const gameInput = interaction.options.getString("game-id", true);
26-
27-
// Extract game ID from argument.
28-
const gameId = GameInfoService.extractGameId(gameInput);
29-
if (!gameId) {
30-
await interaction.editReply(
31-
"Invalid game ID or URL format. Please provide a game ID number or a RetroAchievements game URL.",
32-
);
33-
34-
return;
35-
}
36-
37-
try {
38-
const ganData = await fetchGanData(gameId);
39-
if (!ganData) {
40-
await interaction.editReply(
41-
`Unable to get info from the game ID \`${gameId}\`... :frowning:`,
42-
);
43-
44-
return;
45-
}
46-
47-
const template = TemplateService.generateGanTemplate(
48-
ganData.gameInfo,
49-
ganData.achievementSetDate,
50-
ganData.youtubeLink,
51-
ganData.gameId,
52-
);
53-
54-
await interaction.editReply({
55-
content: `Here's your achievement-news post template:\n${template}`,
56-
});
57-
} catch (error) {
58-
logError("Error in gan slash command:", { error });
59-
await interaction.editReply(
60-
`Unable to get info from the game ID \`${gameId}\`... :frowning:`,
61-
);
62-
}
21+
await runGanCommand(interaction, {
22+
commandName: "gan",
23+
render: (ganData) => ({
24+
content: `Here's your achievement-news post template:\n${TemplateService.generateGanTemplate(
25+
ganData.gameInfo,
26+
ganData.achievementSetDate,
27+
ganData.youtubeLink,
28+
ganData.gameId,
29+
)}`,
30+
}),
31+
});
6332
},
6433
};
6534

src/slash-commands/gan2.command.ts

Lines changed: 12 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
11
import { SlashCommandBuilder } from "discord.js";
22

33
import type { SlashCommand } from "../models";
4-
import { GameInfoService } from "../services/game-info.service";
54
import { TemplateService } from "../services/template.service";
6-
import { fetchGanData } from "../utils/fetch-gan-data";
7-
import { logError } from "../utils/logger";
5+
import { runGanCommand } from "./shared/run-gan-command";
86

97
const gan2SlashCommand: SlashCommand = {
108
data: new SlashCommandBuilder()
@@ -18,45 +16,17 @@ const gan2SlashCommand: SlashCommand = {
1816
),
1917

2018
async execute(interaction, _client) {
21-
await interaction.deferReply();
22-
23-
const gameInput = interaction.options.getString("game-id", true);
24-
25-
// Extract game ID from argument.
26-
const gameId = GameInfoService.extractGameId(gameInput);
27-
if (!gameId) {
28-
await interaction.editReply(
29-
"Invalid game ID or URL format. Please provide a game ID number or a RetroAchievements game URL.",
30-
);
31-
32-
return;
33-
}
34-
35-
try {
36-
const ganData = await fetchGanData(gameId);
37-
if (!ganData) {
38-
await interaction.editReply(
39-
`Unable to get info from the game ID \`${gameId}\`... :frowning:`,
40-
);
41-
42-
return;
43-
}
44-
45-
const output = TemplateService.generateGan2Template(
46-
ganData.gameInfo,
47-
ganData.achievementSetDate,
48-
ganData.youtubeLink,
49-
ganData.gameId,
50-
interaction.user,
51-
);
52-
53-
await interaction.editReply(output);
54-
} catch (error) {
55-
logError("Error in gan2 slash command:", { error });
56-
await interaction.editReply(
57-
`Unable to get info from the game ID \`${gameId}\`... :frowning:`,
58-
);
59-
}
19+
await runGanCommand(interaction, {
20+
commandName: "gan2",
21+
render: (ganData, ix) =>
22+
TemplateService.generateGan2Template(
23+
ganData.gameInfo,
24+
ganData.achievementSetDate,
25+
ganData.youtubeLink,
26+
ganData.gameId,
27+
ix.user,
28+
),
29+
});
6030
},
6131
};
6232

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import type { ChatInputCommandInteraction, InteractionEditReplyOptions } from "discord.js";
2+
3+
import { GameInfoService } from "../../services/game-info.service";
4+
import type { GanData } from "../../utils/fetch-gan-data";
5+
import { fetchGanData } from "../../utils/fetch-gan-data";
6+
import { logError } from "../../utils/logger";
7+
8+
type GanReplyPayload = string | InteractionEditReplyOptions;
9+
10+
type GanRenderer = (ganData: GanData, interaction: ChatInputCommandInteraction) => GanReplyPayload;
11+
12+
interface RunGanCommandOptions {
13+
commandName: "gan" | "gan2";
14+
render: GanRenderer;
15+
}
16+
17+
export const runGanCommand = async (
18+
interaction: ChatInputCommandInteraction,
19+
{ commandName, render }: RunGanCommandOptions,
20+
): Promise<void> => {
21+
await interaction.deferReply();
22+
23+
const gameInput = interaction.options.getString("game-id", true);
24+
25+
const gameId = GameInfoService.extractGameId(gameInput);
26+
// Preserve the original falsy check: extractGameId("0") returns 0, which should be treated as invalid.
27+
if (!gameId) {
28+
await interaction.editReply(
29+
"Invalid game ID or URL format. Please provide a game ID number or a RetroAchievements game URL.",
30+
);
31+
32+
return;
33+
}
34+
35+
try {
36+
const ganData = await fetchGanData(gameId);
37+
if (!ganData) {
38+
await interaction.editReply(
39+
`Unable to get info from the game ID \`${gameId}\`... :frowning:`,
40+
);
41+
42+
return;
43+
}
44+
45+
await interaction.editReply(render(ganData, interaction));
46+
} catch (error) {
47+
logError(`Error in ${commandName} slash command:`, { error });
48+
await interaction.editReply(`Unable to get info from the game ID \`${gameId}\`... :frowning:`);
49+
}
50+
};

0 commit comments

Comments
 (0)