feat(groups): add fail-closed admission management
CI / validate (push) Successful in 5m13s
Release / release (push) Successful in 7m8s

This commit is contained in:
dmg
2026-08-01 18:36:45 -04:00
parent 19a5d04178
commit b88097c15a
32 changed files with 1910 additions and 29 deletions
+40
View File
@@ -46,6 +46,46 @@ export function formatManagedDiscordNickname(
return [...firstName.trim()].slice(0, DISCORD_NICKNAME_LIMIT).join("").trimEnd();
}
export interface DiscordGuildIdentity {
id: string;
username: string;
globalName: string | null;
nickname: string | null;
}
export async function getGuildMemberIdentity(
input: { guildId: string; discordUserId: string; botToken: string },
request: typeof fetch = fetch,
): Promise<DiscordGuildIdentity> {
const response = await request(
`https://discord.com/api/v10/guilds/${input.guildId}/members/${input.discordUserId}`,
{
headers: {
authorization: `Bot ${input.botToken}`,
accept: "application/json",
},
cache: "no-store",
},
);
if (!response.ok) throw new Error(`Discord guild member lookup failed (${response.status})`);
const payload: unknown = await response.json();
if (!payload || typeof payload !== "object") throw new Error("Discord guild member lookup returned invalid data");
const member = payload as { nick?: unknown; user?: unknown };
if (!member.user || typeof member.user !== "object") throw new Error("Discord guild member lookup omitted user data");
const user = member.user as { id?: unknown; username?: unknown; global_name?: unknown };
if (typeof user.id !== "string" || typeof user.username !== "string") {
throw new Error("Discord guild member lookup returned invalid user data");
}
return {
id: user.id,
username: user.username,
globalName: typeof user.global_name === "string" ? user.global_name : null,
nickname: typeof member.nick === "string" ? member.nick : null,
};
}
export async function updateGuildNickname(
input: {
guildId: string;
+21 -1
View File
@@ -1,5 +1,25 @@
import { describe, expect, it, vi } from "vitest";
import { updateGuildNickname } from "../src/index";
import { getGuildMemberIdentity, updateGuildNickname } from "../src/index";
describe("Discord guild identity", () => {
it("reads the member username, global name, nickname, and immutable ID", async () => {
const request = vi.fn<typeof fetch>().mockResolvedValue(new Response(JSON.stringify({
nick: "Sam (Notch)",
user: { id: "987654321098765432", username: "samcraft", global_name: "Sam" },
}), { status: 200, headers: { "content-type": "application/json" } }));
await expect(getGuildMemberIdentity({
guildId: "123456789012345678",
discordUserId: "987654321098765432",
botToken: "secret",
}, request)).resolves.toEqual({
id: "987654321098765432",
username: "samcraft",
globalName: "Sam",
nickname: "Sam (Notch)",
});
});
});
describe("Discord nickname updates", () => {
it("updates a member in the configured guild using bot authentication", async () => {