Skip to content

Commit fc48426

Browse files
authored
create GET /discord-members API (#56)
* create GET /discord-members API * write tests * fixed rsa key issue with new kew pair * remove console log * fix formatting * address comments
1 parent 5c982e5 commit fc48426

File tree

5 files changed

+110
-0
lines changed

5 files changed

+110
-0
lines changed
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import { IRequest } from "itty-router";
2+
import jwt from "@tsndr/cloudflare-worker-jwt";
3+
import * as response from "../constants/responses";
4+
import { env } from "../typeDefinitions/default.types";
5+
import JSONResponse from "../utils/JsonResponse";
6+
import { User } from "../typeDefinitions/user.types";
7+
import { getMembersInServer } from "../utils/getMembersInServer";
8+
9+
export const getMembersInServerHandler = async (
10+
request: IRequest,
11+
env: env
12+
) => {
13+
const authHeader = request.headers.get("Authorization");
14+
15+
if (!authHeader) {
16+
return new JSONResponse(response.BAD_SIGNATURE);
17+
}
18+
try {
19+
const authToken = authHeader.split(" ")[1];
20+
await jwt.verify(authToken, env.RDS_SERVERLESS_PUBLIC_KEY, {
21+
algorithm: "RS256",
22+
});
23+
24+
const users = (await getMembersInServer(env)) as User[];
25+
26+
return new JSONResponse(users);
27+
} catch (err) {
28+
return new JSONResponse(response.BAD_SIGNATURE);
29+
}
30+
};

src/index.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { env } from "./typeDefinitions/default.types";
66
import { discordMessageRequest } from "./typeDefinitions/discordMessage.types";
77
import JSONResponse from "./utils/JsonResponse";
88
import { verifyBot } from "./utils/verifyBot";
9+
import { getMembersInServerHandler } from "./controllers/getMembersInServer";
910

1011
const router = Router();
1112

@@ -15,6 +16,8 @@ router.get("/", async () => {
1516
});
1617
});
1718

19+
router.get("/discord-members", getMembersInServerHandler);
20+
1821
router.post("/", async (request, env) => {
1922
const message: discordMessageRequest = await request.json();
2023
if (message.type === InteractionType.PING) {
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
export interface User {
2+
avatar: null | string;
3+
communication_disabled_until: null | string;
4+
flags: 0;
5+
is_pending: boolean;
6+
joined_at: string; //ISO8601 timestamp
7+
nick: null | string;
8+
pending: boolean;
9+
premium_since: null | string;
10+
roles: string[];
11+
user: {
12+
id: string;
13+
username: string;
14+
global_name: string;
15+
display_name: string;
16+
avatar: string; // avatar hash
17+
discriminator: string; //4-digit discord-tag
18+
public_flags: integer;
19+
avatar_decoration: null;
20+
};
21+
mute: boolean;
22+
deaf: boolean;
23+
}

src/utils/getMembersInServer.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import * as response from "../constants/responses";
2+
import { env } from "../typeDefinitions/default.types";
3+
import { DISCORD_BASE_URL } from "../constants/urls";
4+
5+
export const getMembersInServer = async (env: env) => {
6+
try {
7+
const MEMBERS_URL = `${DISCORD_BASE_URL}/guilds/${env.DISCORD_GUILD_ID}/members?limit=1000`;
8+
const response = await fetch(MEMBERS_URL, {
9+
method: "GET",
10+
headers: {
11+
"Content-Type": "application/json",
12+
Authorization: `Bot ${env.DISCORD_TOKEN}`,
13+
},
14+
});
15+
return response.json();
16+
} catch (err) {
17+
return response.BAD_SIGNATURE;
18+
}
19+
};
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import { DISCORD_BASE_URL } from "../../src/constants/urls";
2+
import JSONResponse from "../../src/utils/JsonResponse";
3+
import { getMembersInServer } from "../../src/utils/getMembersInServer";
4+
5+
describe("getMembersInServer", () => {
6+
const mockEnv = {
7+
BOT_PUBLIC_KEY: "xyz",
8+
DISCORD_GUILD_ID: "123",
9+
DISCORD_TOKEN: "abc",
10+
};
11+
test("returns JSON response with members on success", async () => {
12+
const expectedResponse = [{ id: "1234", name: "John Doe" }];
13+
14+
jest
15+
.spyOn(global, "fetch")
16+
.mockImplementation(() =>
17+
Promise.resolve(new JSONResponse(expectedResponse))
18+
);
19+
20+
const response = await getMembersInServer(mockEnv);
21+
22+
expect(global.fetch).toHaveBeenCalledWith(
23+
`${DISCORD_BASE_URL}/guilds/${mockEnv.DISCORD_GUILD_ID}/members?limit=1000`,
24+
{
25+
method: "GET",
26+
headers: {
27+
"Content-Type": "application/json",
28+
Authorization: `Bot ${mockEnv.DISCORD_TOKEN}`,
29+
},
30+
}
31+
);
32+
33+
expect(response).toEqual(expectedResponse);
34+
});
35+
});

0 commit comments

Comments
 (0)