Skip to content

Commit 9902215

Browse files
Techbot121Meta Construct
authored andcommitted
convert gserv into a "normal slash command"
1 parent dd48fb5 commit 9902215

File tree

1 file changed

+88
-190
lines changed
  • app/services/discord/modules/commands/developer

1 file changed

+88
-190
lines changed
Lines changed: 88 additions & 190 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import * as Discord from "discord.js";
22
import { EphemeralResponse, SlashCommand } from "@/extensions/discord.js";
3-
import GameServer from "@/app/services/gamebridge/GameServer.js";
43
import servers from "@/config/gamebridge.servers.json" with { type: "json" };
54

65
// order matters for the menu
76
const VALID_GSERV_COMMANDS: [string, string][] = [
7+
["qu rehash", "runs both qu and rehash"],
88
["qu", "Quickly updates repositories."],
99
["rehash", "Rehashes the server."],
1010
["merge_repos", "Prepares all repositories for rehash."],
@@ -13,217 +13,115 @@ const VALID_GSERV_COMMANDS: [string, string][] = [
1313
["status", "Shows server status."],
1414
];
1515

16-
const SERVER_EMOJI_MAP = {
17-
"1": "1️⃣",
18-
"2": "2️⃣",
19-
"3": "3️⃣",
20-
"4": "4️⃣",
21-
};
22-
23-
const gserv = async (
24-
ctx: Discord.ButtonInteraction,
25-
gameServer: GameServer,
26-
commands: string[],
27-
solo: boolean,
28-
output: boolean
29-
): Promise<boolean> => {
30-
try {
31-
let buffer = "";
32-
33-
await gameServer.sshExec("gserv", commands, {
34-
stream: "stderr",
35-
onStdout: buff => (buffer += buff),
36-
onStderr: buff => (buffer += buff),
37-
});
38-
39-
const success = !buffer.includes("GSERV FAILED");
40-
41-
const fileName = `${commands.join("_")}_${gameServer.config.id}_${Date.now()}.ansi`;
42-
let msgContent = gameServer.config.name;
43-
if (!success) msgContent += " FAILED";
44-
45-
const response: Discord.BaseMessageOptions = {
46-
content: msgContent,
47-
files: [{ attachment: Buffer.from(buffer), name: fileName }],
48-
};
49-
50-
if (output || success === false) {
51-
solo ? await ctx.editReply(response) : await ctx.followUp(response);
52-
53-
if (solo) {
54-
const msg = await ctx.fetchReply();
55-
await msg.react(success ? "✅" : "❌");
56-
}
57-
} else {
58-
const msg = await ctx.fetchReply();
59-
await msg.react(SERVER_EMOJI_MAP[gameServer.config.id] ?? "❓");
60-
}
61-
return success;
62-
} catch (err) {
63-
const msg = gameServer.config.name + `\ngserv failed!\`\`\`\n${err}\`\`\``;
64-
if (solo) {
65-
await ctx.editReply(msg);
66-
} else {
67-
await ctx.followUp(msg);
68-
}
69-
return false;
70-
}
71-
};
72-
7316
export const SlashGservCommand: SlashCommand = {
7417
options: {
7518
name: "gserv",
7619
description: "Gserv from discord",
7720
default_member_permissions: "0",
21+
options: [
22+
{
23+
type: Discord.ApplicationCommandOptionType.String,
24+
name: "command",
25+
description: "the command to run",
26+
choices: VALID_GSERV_COMMANDS.map(c => {
27+
return { name: c[0], value: c[0] };
28+
}),
29+
required: true,
30+
},
31+
{
32+
type: Discord.ApplicationCommandOptionType.Integer,
33+
name: "server",
34+
description: "The server to run the command on",
35+
choices: servers
36+
.filter(s => !!s.ssh)
37+
.map(s => {
38+
return { name: s.name, value: s.id };
39+
}),
40+
},
41+
{
42+
type: Discord.ApplicationCommandOptionType.Boolean,
43+
name: "show_output",
44+
description: "show gserv output",
45+
},
46+
],
7847
},
7948

8049
async execute(ctx, bot) {
81-
const filter = (i: Discord.Interaction) => i.user.id === ctx.user.id;
8250
const bridge = bot.bridge;
8351
if (!bridge) {
8452
await ctx.reply(EphemeralResponse("GameBridge is missing :("));
8553
console.error(`SlashGserv: GameBridge missing?`, ctx);
8654
return;
8755
}
56+
const selectedServer = ctx.options.getInteger("server");
57+
const servers = selectedServer ? [bridge.servers[selectedServer]] : bridge.servers;
58+
const command = ctx.options.getString("command", true);
59+
const showOutput = ctx.options.getBoolean("show_output") ?? false;
8860

89-
try {
90-
const response = await ctx.reply({
91-
content: "What command do you want to run?",
92-
components: [
93-
{
94-
type: Discord.ComponentType.ActionRow,
95-
components: [
96-
{
97-
type: Discord.ComponentType.StringSelect,
98-
custom_id: "gserv_command",
99-
placeholder: "Choose a command.",
100-
min_values: 1,
101-
max_values: VALID_GSERV_COMMANDS.length,
102-
options: VALID_GSERV_COMMANDS.map(
103-
cmd =>
104-
<Discord.APISelectMenuOption>{
105-
label: cmd[0],
106-
value: cmd[0],
107-
description: cmd[1],
108-
}
109-
),
110-
},
111-
],
112-
},
113-
],
114-
});
115-
116-
const result = await response.awaitMessageComponent({
117-
componentType: Discord.ComponentType.StringSelect,
118-
filter: filter,
119-
time: 60000,
120-
});
121-
const commands = result.values;
61+
const reply = await ctx.deferReply({ withResponse: true });
12262

123-
const server = await result.update({
124-
content: "What server do you want the command to run on?",
125-
components: [
126-
{
127-
type: Discord.ComponentType.ActionRow,
128-
components: [
129-
{
130-
type: Discord.ComponentType.StringSelect,
131-
custom_id: "gserv_server",
132-
placeholder: "Choose a server.",
133-
min_values: 1,
134-
max_values: servers.filter(s => !!s.ssh).length,
135-
options: servers
136-
.filter(s => !!s.ssh)
137-
.map(
138-
server =>
139-
<Discord.APISelectMenuOption>{
140-
label: server.ssh?.host.slice(0, 2), // [g1].metastruct.net
141-
value: server.id.toString(), // g[1].metastruct.net
142-
description: server.name, // g1.metastruct.net
143-
}
144-
),
145-
},
146-
],
147-
},
148-
],
149-
});
63+
await Promise.all(
64+
servers.map(async gameServer => {
65+
const gSDiscord = gameServer.discord;
66+
try {
67+
let buffer = "";
15068

151-
try {
152-
const result = await server.awaitMessageComponent({
153-
componentType: Discord.ComponentType.StringSelect,
154-
filter: filter,
155-
time: 60000,
156-
});
157-
const selectedServers = result.values;
69+
await gameServer.sshExec("gserv", [command], {
70+
stream: "stderr",
71+
onStdout: buff => (buffer += buff),
72+
onStderr: buff => (buffer += buff),
73+
});
15874

159-
const output = await result.update({
160-
content: "Display output?",
161-
components: [
162-
{
163-
type: Discord.ComponentType.ActionRow,
164-
components: [
165-
{
166-
type: Discord.ComponentType.Button,
167-
custom_id: "gserv_output_n",
168-
label: "No",
169-
style: Discord.ButtonStyle.Danger,
170-
},
171-
{
172-
type: Discord.ComponentType.Button,
173-
custom_id: "gserv_output_y",
174-
label: "Yes",
175-
style: Discord.ButtonStyle.Success,
176-
},
177-
],
178-
},
179-
],
180-
});
75+
const success = !buffer.includes("GSERV FAILED");
18176

182-
try {
183-
const result = await output.awaitMessageComponent({
184-
componentType: Discord.ComponentType.Button,
185-
filter: filter,
186-
time: 60000,
187-
});
188-
await result.update({
189-
content: `Running ${commands.join(" and ")} on ${selectedServers
190-
.slice()
191-
.sort()
192-
.join(", ")} please wait...`,
193-
components: [],
194-
});
77+
const fileName = `${command}_${gameServer.config.id}_${Date.now()}.ansi`;
19578

196-
await Promise.all(
197-
bridge.servers
198-
.filter(s => selectedServers.includes(s.config.id.toString()))
199-
.map(gameServer =>
200-
gserv(
201-
result,
202-
gameServer,
203-
commands,
204-
selectedServers.length === 1,
205-
result.customId === "gserv_output_y"
79+
if (showOutput || success === false) {
80+
gSDiscord.rest.post(Discord.Routes.channelMessages(ctx.channelId), {
81+
body: {
82+
content: !success
83+
? "<a:ALERTA:843518761160015933> FAILED <a:ALERTA:843518761160015933> "
84+
: undefined,
85+
message_reference: reply.interaction.responseMessageId
86+
? {
87+
type: 0,
88+
message_id: reply.interaction.responseMessageId,
89+
}
90+
: undefined,
91+
},
92+
files: [{ data: Buffer.from(buffer), name: fileName }],
93+
});
94+
} else {
95+
if (reply.interaction.responseMessageId) {
96+
gSDiscord.rest.put(
97+
Discord.Routes.channelMessageReaction(
98+
ctx.channelId,
99+
reply.interaction.responseMessageId,
100+
"👍"
206101
)
207-
)
208-
)
209-
.then(() => {
210-
if (selectedServers.length === 1) return;
211-
result.editReply(`sent ${commands.join(" and ")} successfully!`);
212-
})
213-
.catch(err =>
214-
result.editReply(`something went wrong!\`\`\`\n${err}\`\`\``)
215-
);
102+
);
103+
}
104+
}
105+
return success;
216106
} catch (err) {
217-
console.error(err);
218-
await ctx.editReply(JSON.stringify(err));
107+
const msg = gameServer.config.name + `\ngserv failed!\`\`\`\n${err}\`\`\``;
108+
gSDiscord.rest.post(Discord.Routes.channelMessages(ctx.channelId), {
109+
body: {
110+
content: `<a:ALERTA:843518761160015933> failed to run gerv <a:ALERTA:843518761160015933>\n\`\`\`${err}\`\`\``,
111+
112+
message_reference: {
113+
type: 0,
114+
message_id: reply.interaction.responseMessageId,
115+
},
116+
},
117+
});
118+
return false;
219119
}
220-
} catch (err) {
221-
console.error(err);
222-
await ctx.deleteReply();
223-
}
224-
} catch (err) {
225-
console.error(err);
226-
await ctx.deleteReply();
227-
}
120+
})
121+
)
122+
.then(() => {
123+
ctx.editReply(`sent \`${command}\` successfully!`);
124+
})
125+
.catch(err => ctx.editReply(`something went wrong!\`\`\`\n${err}\`\`\``));
228126
},
229127
};

0 commit comments

Comments
 (0)