feat: add community role grant and join prompt for players

- Grant Elysian Discord role to players on OAuth login (new and returning)
- Add inGuild flag to Player schema, seeded from role grant response
- Connect Discord Gateway WebSocket to keep inGuild in sync on join/leave
- Return inGuild from load endpoint; expose in game context
- Show join community modal once per session when inGuild is false
This commit is contained in:
2026-03-24 18:10:22 -07:00
committed by Naomi Carrigan
parent b48beef474
commit 5025948530
13 changed files with 549 additions and 3 deletions
+48 -3
View File
@@ -15,6 +15,48 @@ const discordApi = "https://discord.com/api/v10";
*/
const suppressNotifications = 4096;
/**
* Grants the Elysian Discord role to the given player and returns whether they are in the guild.
* Fails silently so role grant errors do not affect the auth flow.
* @param discordId - The Discord user ID to grant the role to.
* @returns True if the player is in the guild and the role was granted, false otherwise.
*/
const grantElysianRole = async(discordId: string): Promise<boolean> => {
const botToken = process.env.DISCORD_BOT_TOKEN;
const guildId = process.env.DISCORD_GUILD_ID;
const roleId = process.env.DISCORD_ELYSIAN_ROLE_ID;
if (
botToken === undefined || botToken === ""
|| guildId === undefined || guildId === ""
|| roleId === undefined || roleId === ""
) {
return false;
}
try {
const response = await fetch(
`${discordApi}/guilds/${guildId}/members/${discordId}/roles/${roleId}`,
{
headers: {
"Authorization": `Bot ${botToken}`,
"User-Agent": "DiscordBot (https://elysium.nhcarrigan.com, 1.0.0)",
},
method: "PUT",
},
);
return response.ok || response.status === 204;
} catch (error) {
void logger.error(
"webhook_elysian_role",
error instanceof Error
? error
: new Error(String(error)),
);
return false;
}
};
/**
* Grants the apotheosis Discord role to the given player if configured.
* Fails silently so role grant errors do not affect the game action.
@@ -38,8 +80,11 @@ const grantApotheosisRole = async(discordId: string): Promise<void> => {
await fetch(
`${discordApi}/guilds/${guildId}/members/${discordId}/roles/${roleId}`,
{
headers: { Authorization: `Bot ${botToken}` },
method: "PUT",
headers: {
"Authorization": `Bot ${botToken}`,
"User-Agent": "DiscordBot (https://elysium.nhcarrigan.com, 1.0.0)",
},
method: "PUT",
},
);
} catch (error) {
@@ -109,4 +154,4 @@ const postMilestoneWebhook = async(
}
};
export { grantApotheosisRole, postMilestoneWebhook };
export { grantApotheosisRole, grantElysianRole, postMilestoneWebhook };