205 lines
6.1 KiB
TypeScript
205 lines
6.1 KiB
TypeScript
import type { VercelRequest, VercelResponse } from "@vercel/node";
|
|
|
|
interface CommandData {
|
|
name: string;
|
|
description: string;
|
|
options?: any[];
|
|
}
|
|
|
|
// Define all commands that should be registered
|
|
const COMMANDS: CommandData[] = [
|
|
{
|
|
name: "verify",
|
|
description: "Link your Discord account to AeThex",
|
|
},
|
|
{
|
|
name: "set-realm",
|
|
description: "Choose your primary arm/realm (Labs, GameForge, Corp, etc.)",
|
|
options: [
|
|
{
|
|
name: "realm",
|
|
type: 3,
|
|
description: "Your primary realm",
|
|
required: true,
|
|
choices: [
|
|
{ name: "Labs", value: "labs" },
|
|
{ name: "GameForge", value: "gameforge" },
|
|
{ name: "Corp", value: "corp" },
|
|
{ name: "Foundation", value: "foundation" },
|
|
{ name: "Dev-Link", value: "devlink" },
|
|
],
|
|
},
|
|
],
|
|
},
|
|
{
|
|
name: "profile",
|
|
description: "View your linked AeThex profile",
|
|
},
|
|
{
|
|
name: "unlink",
|
|
description: "Disconnect your Discord account from AeThex",
|
|
},
|
|
{
|
|
name: "verify-role",
|
|
description: "Check your assigned Discord roles",
|
|
},
|
|
];
|
|
|
|
const DISCORD_API_VERSION = "10";
|
|
const DISCORD_API_URL = `https://discord.com/api/v${DISCORD_API_VERSION}`;
|
|
|
|
export default async function handler(req: VercelRequest, res: VercelResponse) {
|
|
try {
|
|
// Allow both GET and POST
|
|
if (req.method !== "POST" && req.method !== "GET") {
|
|
res.setHeader("Allow", "GET, POST");
|
|
return res.status(405).json({ error: "Method not allowed" });
|
|
}
|
|
|
|
// Basic security: Check if requester has admin token
|
|
const authHeader = req.headers.authorization;
|
|
const queryToken = req.query.token;
|
|
const adminToken = process.env.DISCORD_ADMIN_REGISTER_TOKEN;
|
|
|
|
const providedToken = authHeader
|
|
? authHeader.replace("Bearer ", "")
|
|
: queryToken;
|
|
|
|
if (!adminToken || providedToken !== adminToken) {
|
|
return res.status(401).json({ error: "Unauthorized - Invalid token" });
|
|
}
|
|
|
|
// Validate environment variables
|
|
const requiredVars = ["DISCORD_BOT_TOKEN", "DISCORD_CLIENT_ID"];
|
|
const missingVars = requiredVars.filter((v) => !process.env[v]);
|
|
|
|
if (missingVars.length > 0) {
|
|
return res.status(500).json({
|
|
error: "Missing environment variables",
|
|
missing: missingVars,
|
|
});
|
|
}
|
|
|
|
const botToken = process.env.DISCORD_BOT_TOKEN!;
|
|
const clientId = process.env.DISCORD_CLIENT_ID!;
|
|
const authorizationHeader = `Bot ${botToken}`;
|
|
|
|
console.log(`📝 Registering ${COMMANDS.length} Discord slash commands...`);
|
|
|
|
// Try bulk update first
|
|
try {
|
|
const bulkResponse = await fetch(
|
|
`${DISCORD_API_URL}/applications/${clientId}/commands`,
|
|
{
|
|
method: "PUT",
|
|
headers: {
|
|
Authorization: authorizationHeader,
|
|
"Content-Type": "application/json",
|
|
},
|
|
body: JSON.stringify(COMMANDS),
|
|
},
|
|
);
|
|
|
|
if (bulkResponse.ok) {
|
|
const data = (await bulkResponse.json()) as any[];
|
|
console.log(`✅ Successfully registered ${data.length} slash commands`);
|
|
|
|
return res.status(200).json({
|
|
success: true,
|
|
message: `Registered ${data.length} slash commands`,
|
|
commands: (data as any[]).map((cmd: any) => cmd.name),
|
|
});
|
|
}
|
|
|
|
// If bulk update failed, try individual registration
|
|
const errorData = (await bulkResponse.json()) as any;
|
|
const errorCode = errorData?.code;
|
|
|
|
if (errorCode === 50240) {
|
|
// Error 50240: Entry Point conflict (Discord Activity enabled)
|
|
console.warn(
|
|
"⚠️ Error 50240: Entry Point detected. Registering individually...",
|
|
);
|
|
|
|
const results = [];
|
|
let successCount = 0;
|
|
let skipCount = 0;
|
|
|
|
for (const command of COMMANDS) {
|
|
try {
|
|
const postResponse = await fetch(
|
|
`${DISCORD_API_URL}/applications/${clientId}/commands`,
|
|
{
|
|
method: "POST",
|
|
headers: {
|
|
Authorization: authorizationHeader,
|
|
"Content-Type": "application/json",
|
|
},
|
|
body: JSON.stringify(command),
|
|
},
|
|
);
|
|
|
|
if (postResponse.ok) {
|
|
const posted = await postResponse.json();
|
|
results.push({
|
|
name: command.name,
|
|
status: "registered",
|
|
id: posted.id,
|
|
});
|
|
successCount++;
|
|
} else if (postResponse.status === 400) {
|
|
// Error 50045: Command already exists
|
|
results.push({
|
|
name: command.name,
|
|
status: "already_exists",
|
|
});
|
|
skipCount++;
|
|
} else {
|
|
const errData = await postResponse.json();
|
|
results.push({
|
|
name: command.name,
|
|
status: "error",
|
|
error: errData.message || `HTTP ${postResponse.status}`,
|
|
});
|
|
}
|
|
} catch (postError: any) {
|
|
results.push({
|
|
name: command.name,
|
|
status: "error",
|
|
error: postError.message,
|
|
});
|
|
}
|
|
}
|
|
|
|
console.log(
|
|
`✅ Registration complete: ${successCount} new, ${skipCount} already existed`,
|
|
);
|
|
|
|
return res.status(200).json({
|
|
success: true,
|
|
message: `Registered ${successCount} new commands (${skipCount} already existed)`,
|
|
results,
|
|
note: "Entry Point command is managed by Discord (Activities enabled)",
|
|
});
|
|
}
|
|
|
|
throw new Error(
|
|
`Discord API error: ${errorData?.message || bulkResponse.statusText}`,
|
|
);
|
|
} catch (error: any) {
|
|
console.error("❌ Failed to register commands:", error);
|
|
|
|
return res.status(500).json({
|
|
success: false,
|
|
error: error?.message || "Failed to register commands",
|
|
});
|
|
}
|
|
} catch (error: any) {
|
|
console.error("❌ Unexpected error:", error);
|
|
|
|
return res.status(500).json({
|
|
success: false,
|
|
error: error?.message || "Internal server error",
|
|
});
|
|
}
|
|
}
|