Skip to content

Commit

Permalink
Feat : Updated mention each flow
Browse files Browse the repository at this point in the history
  • Loading branch information
joyguptaa committed Mar 7, 2024
1 parent 1db2a5e commit 49ee62c
Show file tree
Hide file tree
Showing 8 changed files with 267 additions and 15 deletions.
6 changes: 4 additions & 2 deletions src/controllers/baseHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,8 @@ import {

export async function baseHandler(
message: discordMessageRequest,
env: env
env: env,
ctx: ExecutionContext
): Promise<JSONResponse> {
const command = lowerCaseMessageCommands(message);

Expand All @@ -66,8 +67,9 @@ export async function baseHandler(
const transformedArgument = {
roleToBeTaggedObj: data[0],
displayMessageObj: data[1] ?? {},
channelId: message.channel_id,
};
return await mentionEachUser(transformedArgument, env);
return await mentionEachUser(transformedArgument, env, ctx);
}

case getCommandName(LISTENING): {
Expand Down
30 changes: 24 additions & 6 deletions src/controllers/mentionEachUser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,16 @@ import {
UserArray,
MentionEachUserOptions,
} from "../typeDefinitions/filterUsersByRole";
import { checkDisplayType } from "../utils/checkDisplayType";
import { mentionEachUserInMessage } from "../utils/guildRole";

export async function mentionEachUser(
transformedArgument: {
roleToBeTaggedObj: MentionEachUserOptions;
displayMessageObj?: MentionEachUserOptions;
channelId: number;
},
env: env
env: env,
ctx: ExecutionContext
) {
const getMembersInServerResponse = await getMembersInServer(env);
const roleId = transformedArgument.roleToBeTaggedObj.value;
Expand All @@ -24,9 +26,25 @@ export async function mentionEachUser(
getMembersInServerResponse as UserArray[],
roleId
);
const responseData = checkDisplayType({
const payload = {
channelId: transformedArgument.channelId,
roleId: roleId,
message: msgToBeSent,
usersWithMatchingRole,
msgToBeSent,
});
return discordTextResponse(responseData);
};
if (usersWithMatchingRole.length === 0) {
return discordTextResponse("Sorry no user found under this role.");
} else {
ctx.waitUntil(
mentionEachUserInMessage({
message: payload.message,
userIds: payload.usersWithMatchingRole,
channelId: payload.channelId,
env,
})
);
return discordTextResponse(
`Found ${usersWithMatchingRole.length} users with matched role, mentioning them shortly...`
);
}
}
12 changes: 8 additions & 4 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ router.delete("/roles", removeGuildRoleHandler);

router.post("/profile/blocked", sendProfileBlockedMessage);

router.post("/", async (request, env) => {
router.post("/", async (request, env, ctx: ExecutionContext) => {
const message: discordMessageRequest = await request.json();

if (message.type === InteractionType.PING) {
Expand All @@ -66,7 +66,7 @@ router.post("/", async (request, env) => {
});
}
if (message.type === InteractionType.APPLICATION_COMMAND) {
return baseHandler(message, env);
return baseHandler(message, env, ctx);
}
return new JSONResponse(response.UNKNOWN_INTERACTION, { status: 400 });
});
Expand All @@ -78,7 +78,11 @@ router.all("*", async () => {
});

export default {
async fetch(request: Request, env: env): Promise<Response> {
async fetch(
request: Request,
env: env,
ctx: ExecutionContext
): Promise<Response> {
const apiUrls = ["/invite", "/roles", "/profile/blocked"];
const url = new URL(request.url);
if (request.method === "POST" && !apiUrls.includes(url.pathname)) {
Expand All @@ -87,7 +91,7 @@ export default {
return new JSONResponse(response.BAD_SIGNATURE, { status: 401 });
}
}
return router.handle(request, env);
return router.handle(request, env, ctx);
},

async scheduled(req: Request, env: env, ctx: ExecutionContext) {
Expand Down
53 changes: 53 additions & 0 deletions src/typeDefinitions/discordMessage.types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,59 @@ export interface discordMessageRequest {
data: messageRequestData;
member: messageRequestMember;
guild_id: number;
channel_id: number;
}

export interface DiscordMessageResponse {
id: string;
type: number;
content: string;
channel_id: string;
author: {
id: string;
username: string;
avatar: string | null;
discriminator: string;
public_flags: number;
premium_type: number;
flags: number;
bot: boolean;
banner: string | null;
accent_color: string | null;
global_name: string | null;
avatar_decoration_data: string | null;
banner_color: string | null;
};
attachments: Array<string>;
embeds: Array<string>;
mentions: {
id: string;
username: string;
avatar: string | null;
discriminator: string;
public_flags: number;
premium_type: number;
flags: number;
banner: string | null;
accent_color: string | null;
global_name: string | null;
avatar_decoration_data: string | null;
banner_color: string | null;
}[];
mention_roles: Array<string>;
pinned: boolean;
mention_everyone: boolean;
tts: boolean;
timestamp: string;
edited_timestamp: string | null;
flags: number;
components: Array<string>;
referenced_message: Arra<string> | null;
}

export interface discordMessageError {
code: number;
message: string;
}

export interface messageRequestData {
Expand Down
63 changes: 63 additions & 0 deletions src/utils/guildRole.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@ import {
import { DISCORD_BASE_URL } from "../constants/urls";
import { env } from "../typeDefinitions/default.types";
import {
DiscordMessageResponse,
createNewRole,
discordMessageError,
discordMessageRequest,
guildRoleResponse,
memberGroupRole,
} from "../typeDefinitions/discordMessage.types";
Expand Down Expand Up @@ -135,3 +138,63 @@ export async function getGuildRoleByName(
const roles = await getGuildRoles(env);
return roles?.find((role) => role.name === roleName);
}

export async function mentionEachUserInMessage({
message,
userIds,
channelId,
env,
}: {
message?: string;
userIds: string[];
channelId: number;
env: env;
}) {
const batchSize = 10;
let failedAPICalls = 0;
try {
for (let i = 0; i < userIds.length; i += batchSize) {
const batchwiseUserIds = userIds.slice(i, i + batchSize);
const messageRequest = batchwiseUserIds.map((userId) => {
return fetch(`${DISCORD_BASE_URL}/channels/${channelId}/messages`, {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bot ${env.DISCORD_TOKEN}`,
},
body: JSON.stringify({
content: `${message ? message + " " : ""} ${userId}`,
}),
}).then((response) => response.json()) as Promise<
discordMessageRequest | discordMessageError
>;
});
const responses = await Promise.all(messageRequest);
responses.forEach((response) => {
if (
response &&
"message" in response &&
response.message === "404: Not Found"
) {
failedAPICalls += 1;
console.error(`Failed to mention a user`);
}
});
await new Promise((resolve) => setTimeout(resolve, 1000));
}
if (failedAPICalls > 0) {
await fetch(`${DISCORD_BASE_URL}/channels/${channelId}/messages`, {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bot ${env.DISCORD_TOKEN}`,
},
body: JSON.stringify({
content: `Failed to tag ${failedAPICalls} users`,
}),
});
}
} catch (error) {
console.log("Error occured while running mentionEachUserInMessage", error);
}
}
58 changes: 58 additions & 0 deletions tests/fixtures/fixture.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ export const dummyHelloMessage: discordMessageRequest = {
},
},
guild_id: 123456,
channel_id: 123456,
};

export const dummyVerifyMessage: discordMessageRequest = {
Expand All @@ -39,6 +40,7 @@ export const dummyVerifyMessage: discordMessageRequest = {
},
},
guild_id: 123456,
channel_id: 123456,
};

export const dummyCreateBody: createNewRole = {
Expand Down Expand Up @@ -95,6 +97,7 @@ export const transformedArgument = {
value: "1118201414078976192",
},
displayMessageObj: { name: "message", type: 3, value: "hello" },
channelId: 1244,
};

export const onlyRoleToBeTagged = {
Expand All @@ -103,8 +106,14 @@ export const onlyRoleToBeTagged = {
type: 8,
value: "1118201414078976192",
},
channelId: 1244,
};

export const ctx = {
/* eslint-disable @typescript-eslint/no-empty-function */
waitUntil: (promise: void | Promise<void>): void => {},
passThroughOnException: (): void => {},
};
export const generateDummyRequestObject = ({
url,
method,
Expand Down Expand Up @@ -165,6 +174,55 @@ export const discordUserData = {
},
};

export const mockMessageResponse = {
id: "1215369965792665620",
type: 0,
content: "<@849364584674492426>",
channel_id: "868936963456126991",
author: {
id: "1205843978088620144",
username: "Joy Bot",
avatar: null,
discriminator: "7363",
public_flags: 524288,
premium_type: 0,
flags: 524288,
bot: true,
banner: null,
accent_color: null,
global_name: null,
avatar_decoration_data: null,
banner_color: null,
},
attachments: [],
embeds: [],
mentions: [
{
id: "849364584674492426",
username: "Aniket",
avatar: null,
discriminator: "1514",
public_flags: 0,
premium_type: 0,
flags: 0,
banner: null,
accent_color: null,
global_name: null,
avatar_decoration_data: null,
banner_color: null,
},
],
mention_roles: [],
pinned: false,
mention_everyone: false,
tts: false,
timestamp: "2024-03-07T18:46:20.327000+00:00",
edited_timestamp: null,
flags: 0,
components: [],
referenced_message: null,
};

export const userBackendMock: UserBackend = {
message: "User returned successfully",
user: {
Expand Down
13 changes: 10 additions & 3 deletions tests/unit/handlers/mentionEachUser.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { filterUserByRoles } from "../../../src/utils/filterUsersByRole";
import {
onlyRoleToBeTagged,
transformedArgument,
ctx,
} from "../../fixtures/fixture";

describe("Test mention each function", () => {
Expand All @@ -14,19 +15,25 @@ describe("Test mention each function", () => {
DISCORD_TOKEN: "abc",
};

const response = mentionEachUser(transformedArgument, env);
const response = mentionEachUser(transformedArgument, env, ctx);
expect(response).toBeInstanceOf(Promise);
});

it("should run without displayMessageObj argument", () => {
it("should run without displayMessageObj argument", async () => {
const env = {
BOT_PUBLIC_KEY: "xyz",
DISCORD_GUILD_ID: "123",
DISCORD_TOKEN: "abc",
};

const response = mentionEachUser(onlyRoleToBeTagged, env);
const response = mentionEachUser(onlyRoleToBeTagged, env, ctx);
expect(response).toBeInstanceOf(Promise);
const textMessage: { data: { content: string } } = await response.then(
(res) => res.json()
);
expect(textMessage.data.content).toBe(
"Sorry no user found under this role."
);
});

it("should return users with matching roles", () => {
Expand Down
Loading

0 comments on commit 49ee62c

Please sign in to comment.