Skip to content

Commit f52e238

Browse files
authored
Merge pull request #55 from Real-Dev-Squad/feature/discord-roles
Feature/discord roles
2 parents fc48426 + 1e3a4db commit f52e238

File tree

11 files changed

+258
-5
lines changed

11 files changed

+258
-5
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,9 @@
3535
"wrangler": "^2.5.0"
3636
},
3737
"dependencies": {
38+
"dotenv": "^16.0.3",
3839
"@tsndr/cloudflare-worker-jwt": "^2.2.1",
3940
"discord-interactions": "^3.2.0",
40-
"dotenv": "^16.0.3",
4141
"itty-router": "^3.0.11",
4242
"node-fetch": "^3.3.0"
4343
},

src/constants/responses.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,3 +21,5 @@ export const INTERNAL_SERVER_ERROR =
2121

2222
export const RETRY_COMMAND =
2323
"Oops, we didn't catch that! Please use the command again.";
24+
25+
export const ROLE_ADDED = "Role added successfully";

src/constants/urls.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
export const RDS_BASE_API_URL = "https://api.realdevsquad.com";
22
export const RDS_BASE_STAGING_API_URL = "https://staging-api.realdevsquad.com";
3-
export const RDS_BASE_DEVELOPMENT_API_URL = "http://localhost:3000"; // If needed, modify the URL to your local API server
3+
export const RDS_BASE_DEVELOPMENT_API_URL = "YOUR_LOCAL_API_URL"; // If needed, modify the URL to your local API server
44

55
export const DISCORD_BASE_URL = "https://discord.com/api/v10";
66
export const DISCORD_AVATAR_BASE_URL = "https://cdn.discordapp.com/avatars";
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import * as response from "../constants/responses";
2+
import { env } from "../typeDefinitions/default.types";
3+
import JSONResponse from "../utils/JsonResponse";
4+
import { IRequest } from "itty-router";
5+
import { addGroupRole, createGuildRole } from "../utils/guildRole";
6+
import {
7+
createNewRole,
8+
memberGroupRole,
9+
} from "../typeDefinitions/discordMessage.types";
10+
import { verifyAuthToken } from "../utils/verifyAuthToken";
11+
12+
export async function createGuildRoleHandler(request: IRequest, env: env) {
13+
const authHeader = request.headers.get("Authorization");
14+
if (!authHeader) {
15+
return new JSONResponse(response.BAD_SIGNATURE);
16+
}
17+
try {
18+
await verifyAuthToken(authHeader, env);
19+
const body: createNewRole = await request.json();
20+
21+
const res = await createGuildRole(body, env);
22+
return new JSONResponse(res);
23+
} catch (err) {
24+
return new JSONResponse(response.BAD_SIGNATURE);
25+
}
26+
}
27+
export async function addGroupRoleHandler(request: IRequest, env: env) {
28+
const authHeader = request.headers.get("Authorization");
29+
if (!authHeader) {
30+
return new JSONResponse(response.BAD_SIGNATURE);
31+
}
32+
try {
33+
await verifyAuthToken(authHeader, env);
34+
const body: memberGroupRole = await request.json();
35+
36+
const res = await addGroupRole(body, env);
37+
return new JSONResponse(res);
38+
} catch (err) {
39+
return new JSONResponse(response.BAD_SIGNATURE);
40+
}
41+
}

src/index.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ 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 {
10+
addGroupRoleHandler,
11+
createGuildRoleHandler,
12+
} from "./controllers/guildRoleHandler";
913
import { getMembersInServerHandler } from "./controllers/getMembersInServer";
1014

1115
const router = Router();
@@ -16,6 +20,10 @@ router.get("/", async () => {
1620
});
1721
});
1822

23+
router.put("/roles/create", createGuildRoleHandler);
24+
25+
router.put("/roles/add", addGroupRoleHandler);
26+
1927
router.get("/discord-members", getMembersInServerHandler);
2028

2129
router.post("/", async (request, env) => {

src/typeDefinitions/discordMessage.types.d.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,3 +30,25 @@ export interface messageMember {
3030
export interface createDmChannel {
3131
id: number;
3232
}
33+
export interface createNewRole {
34+
rolename: string;
35+
mentionable: boolean;
36+
}
37+
export interface memberGroupRole {
38+
userid: string;
39+
roleid: string;
40+
}
41+
42+
export interface guildRoleResponse {
43+
id: string;
44+
name: string;
45+
color: number;
46+
hoist: boolean;
47+
icon?: string;
48+
unicode_emoji?: string;
49+
position: number;
50+
permissions: string;
51+
managed: boolean;
52+
mentionable: boolean;
53+
tags?: object;
54+
}

src/utils/guildRole.ts

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import { INTERNAL_SERVER_ERROR, ROLE_ADDED } from "../constants/responses";
2+
import { DISCORD_BASE_URL } from "../constants/urls";
3+
import { env } from "../typeDefinitions/default.types";
4+
import {
5+
createNewRole,
6+
guildRoleResponse,
7+
memberGroupRole,
8+
} from "../typeDefinitions/discordMessage.types";
9+
10+
export async function createGuildRole(
11+
body: createNewRole,
12+
env: env
13+
): Promise<guildRoleResponse | string> {
14+
const createGuildRoleUrl = `${DISCORD_BASE_URL}/guilds/${env.DISCORD_GUILD_ID}/roles`;
15+
const data = {
16+
...body,
17+
name: body.rolename,
18+
};
19+
try {
20+
const response = await fetch(createGuildRoleUrl, {
21+
method: "POST",
22+
headers: {
23+
"Content-Type": "application/json",
24+
Authorization: `Bot ${env.DISCORD_TOKEN}`,
25+
},
26+
body: JSON.stringify(data),
27+
});
28+
if (response.ok) {
29+
return await response.json();
30+
} else {
31+
return INTERNAL_SERVER_ERROR;
32+
}
33+
} catch (err) {
34+
return INTERNAL_SERVER_ERROR;
35+
}
36+
}
37+
38+
export async function addGroupRole(body: memberGroupRole, env: env) {
39+
const { userid, roleid } = body;
40+
const createGuildRoleUrl = `${DISCORD_BASE_URL}/guilds/${env.DISCORD_GUILD_ID}/members/${userid}/roles/${roleid}`;
41+
try {
42+
const response = await fetch(createGuildRoleUrl, {
43+
method: "PUT",
44+
headers: {
45+
"Content-Type": "application/json",
46+
Authorization: `Bot ${env.DISCORD_TOKEN}`,
47+
},
48+
});
49+
if (response.ok) {
50+
return { message: ROLE_ADDED };
51+
} else {
52+
return INTERNAL_SERVER_ERROR;
53+
}
54+
} catch (err) {
55+
return INTERNAL_SERVER_ERROR;
56+
}
57+
}

src/utils/verifyAuthToken.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { env } from "../typeDefinitions/default.types";
2+
import jwt from "@tsndr/cloudflare-worker-jwt";
3+
4+
/**
5+
*
6+
* @param authHeader { string } : the auth header of request
7+
* @param env { env }: the ctx (context) which contains the secrets put in as wrangler secrets.
8+
*/
9+
10+
export async function verifyAuthToken(authHeader: string, env: env) {
11+
const authToken = authHeader.split(" ")[1];
12+
await jwt.verify(authToken, env.RDS_SERVERLESS_PUBLIC_KEY, {
13+
algorithm: "RS256",
14+
});
15+
}

src/utils/verifyBot.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import { verifyKey } from "discord-interactions";
22
import { env } from "../typeDefinitions/default.types";
3-
43
/**
54
*
65
* @param request { Request } : request the worker receives
@@ -23,6 +22,5 @@ export async function verifyBot(request: Request, env: env) {
2322
timestamp,
2423
env.DISCORD_PUBLIC_KEY
2524
);
26-
2725
return isValidRequest;
2826
}

tests/fixtures/fixture.ts

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1-
import { discordMessageRequest } from "../../src/typeDefinitions/discordMessage.types";
1+
import {
2+
createNewRole,
3+
discordMessageRequest,
4+
memberGroupRole,
5+
} from "../../src/typeDefinitions/discordMessage.types";
26
import { InteractionType } from "discord-interactions";
37

48
export const dummyHelloMessage: discordMessageRequest = {
@@ -32,3 +36,18 @@ export const dummyVerifyMessage: discordMessageRequest = {
3236
},
3337
guild_id: 123456,
3438
};
39+
40+
export const dummyCreateBody: createNewRole = {
41+
rolename: "test role",
42+
mentionable: true,
43+
};
44+
45+
export const dummyAddRoleBody: memberGroupRole = {
46+
userid: "abcd1234",
47+
roleid: "defg5678",
48+
};
49+
50+
export const guildEnv = {
51+
DISCORD_GUILD_ID: "1234",
52+
DISCORD_TOKEN: "abcd",
53+
};

0 commit comments

Comments
 (0)