Skip to content

Commit cff00f5

Browse files
shreya-mishrafakhruddinkwsahsisunnyvinit717prakashchoudhary07
authored
Dev to Main (RealDevSquad#128)
* Auto fetch task which are overdue * added type definitions for taskoverdue, refactored the code and removed the use of any * fixed return type of scheduled func * added fixtures and tests for getDiscordIds func * updated type def for user backend * removed public key from .toml and removed kick command from baseHandler * removed public key * added error message in getDiscordIds.ts * added fixture for taskOverdue, written test for taskOverDue, restructured the code of scheduleEventHandler, altered tests for getDiscordIds, refactored code for taskOverDueDiscordMembers, refactored code for getDiscordIds * forgot to add updated typeDefinitions for taskOverdue * extracted url to url.ts * resolved comments by:bharti * resolving comments * keeping schedule event under comments * error handling in scheduledEventHandler * resolved comments by:bharti * resolving comments by:dipayan * replaced staging url with production * inserted tracking channel id in url * extracted super user discord id to constants * BugFix: /listening (RealDevSquad#126) * bug fix * refactored te code * major fixes * resolved the comments --------- Co-authored-by: Prakash Choudhary <[email protected]> --------- Co-authored-by: FMK2312 <[email protected]> Co-authored-by: Sunny Sahsi <[email protected]> Co-authored-by: Fakhruddin KW <[email protected]> Co-authored-by: Vinit khandal <[email protected]> Co-authored-by: Prakash Choudhary <[email protected]>
1 parent 274fe3d commit cff00f5

File tree

14 files changed

+368
-13
lines changed

14 files changed

+368
-13
lines changed

src/constants/urls.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,6 @@ export const VERIFICATION_SITE_URL = "https://my.realdevsquad.com";
99
export const DEVELOPMENT_VERIFICATION_SITE_URL = "http://localhost:3443";
1010
export const STAGING_VERIFICATION_SITE_URL =
1111
"https://staging-my.realdevsquad.com";
12+
13+
export const RDS_TRACKING_CHANNEL_URL =
14+
"https://discord.com/api/v10/channels/1136650427878871090/messages";

src/constants/variables.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export const SUPER_USER_ONE = "154585730465660929";
2+
export const SUPER_USER_TWO = "1040700289348542566";

src/controllers/baseHandler.ts

Lines changed: 32 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -60,26 +60,47 @@ export async function baseHandler(
6060
const data = message.data?.options;
6161
const setter = data ? data[0].value : false;
6262
const nickname = removeListening(message.member.nick || "");
63+
let discordEphemeral;
64+
let updateNickNameData = "";
6365
try {
6466
if (setter) {
65-
if (!message.member.nick?.includes(NICKNAME_SUFFIX)) {
66-
await updateNickName(
67-
`${message.member.user.id}`,
68-
NICKNAME_PREFIX + message.member.nick + NICKNAME_SUFFIX,
69-
env
70-
);
71-
return discordEphemeralResponse(LISTENING_SUCCESS_MESSAGE);
67+
if (
68+
message.member.nick &&
69+
!message.member.nick.includes(NICKNAME_SUFFIX)
70+
) {
71+
updateNickNameData =
72+
NICKNAME_PREFIX + message.member.nick + NICKNAME_SUFFIX;
73+
discordEphemeral = LISTENING_SUCCESS_MESSAGE;
74+
} else if (!message.member.nick) {
75+
(updateNickNameData = NICKNAME_PREFIX + "" + NICKNAME_SUFFIX),
76+
(discordEphemeral = LISTENING_SUCCESS_MESSAGE);
7277
} else {
73-
return discordEphemeralResponse(ALREADY_LISTENING);
78+
updateNickNameData = message.member.nick;
79+
discordEphemeral = ALREADY_LISTENING;
7480
}
81+
} else if (
82+
!setter &&
83+
!message.member.nick &&
84+
message.member.user.username.includes(NICKNAME_SUFFIX)
85+
) {
86+
updateNickNameData = message.member.user.username + NICKNAME_SUFFIX;
87+
88+
discordEphemeral = REMOVED_LISTENING_MESSAGE;
7589
} else {
7690
if (message.member.nick?.includes(NICKNAME_SUFFIX)) {
77-
await updateNickName(`${message.member.user.id}`, nickname, env);
78-
return discordEphemeralResponse(REMOVED_LISTENING_MESSAGE);
91+
updateNickNameData = nickname;
92+
discordEphemeral = REMOVED_LISTENING_MESSAGE;
7993
} else {
80-
return discordEphemeralResponse(NOTHING_CHANGED);
94+
updateNickNameData = nickname;
95+
discordEphemeral = NOTHING_CHANGED;
8196
}
8297
}
98+
await updateNickName(
99+
message.member.user.id.toString(),
100+
updateNickNameData,
101+
env
102+
);
103+
return discordEphemeralResponse(discordEphemeral);
83104
} catch (err) {
84105
return discordEphemeralResponse(RETRY_COMMAND);
85106
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import { env } from "../typeDefinitions/default.types";
2+
import { taskOverDueDiscordMembers } from "../utils/taskOverDueDiscordMembers";
3+
import * as error from "../constants/responses";
4+
import { getDiscordIds } from "../utils/getDiscordIds";
5+
import { RDS_TRACKING_CHANNEL_URL } from "../constants/urls";
6+
import { SUPER_USER_ONE, SUPER_USER_TWO } from "../constants/variables";
7+
8+
export async function send(env: env): Promise<void> {
9+
try {
10+
const assigneeIds: string[] | string = await taskOverDueDiscordMembers();
11+
12+
//A user might have more than one task which are running red
13+
//so to mention them just once, we are using Set to filter out
14+
const discordIds: string[] | string = await getDiscordIds(assigneeIds);
15+
const uniqueDiscordIds = [...new Set(discordIds)];
16+
17+
//notifying the two users with the authority.
18+
let stringToBeSent = `<@${SUPER_USER_ONE}> <@${SUPER_USER_TWO}>\nThese people have their task running red:\n`;
19+
20+
let forFormatting = 0;
21+
uniqueDiscordIds.forEach((id) => {
22+
const discordUser = `<@${id}> `;
23+
stringToBeSent += discordUser;
24+
forFormatting++;
25+
if (forFormatting === 3) {
26+
//to keep 3 users/line
27+
forFormatting = 0;
28+
stringToBeSent += `\n`;
29+
}
30+
});
31+
32+
if (
33+
assigneeIds === error.INTERNAL_SERVER_ERROR ||
34+
discordIds === error.INTERNAL_SERVER_ERROR
35+
) {
36+
throw new Error(error.INTERNAL_SERVER_ERROR);
37+
}
38+
39+
const bodyObj = {
40+
content: stringToBeSent,
41+
};
42+
43+
const url = `${RDS_TRACKING_CHANNEL_URL}`;
44+
45+
const res = await fetch(url, {
46+
method: "POST",
47+
body: JSON.stringify(bodyObj),
48+
headers: {
49+
"Content-Type": "application/json",
50+
Authorization: `Bot ${env.DISCORD_TOKEN}`,
51+
},
52+
});
53+
} catch (e) {
54+
console.log(e);
55+
}
56+
}

src/index.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import {
1616
import { getMembersInServerHandler } from "./controllers/getMembersInServer";
1717
import { changeNickname } from "./controllers/changeNickname";
1818
import { getGuildMemberDetailsHandler } from "./controllers/getGuildMemberDetailsHandler";
19+
import { send } from "./handlers/scheduledEventHandler";
1920

2021
const router = Router();
2122

@@ -50,6 +51,7 @@ router.delete("/roles", removeGuildRoleHandler);
5051

5152
router.post("/", async (request, env) => {
5253
const message: discordMessageRequest = await request.json();
54+
5355
if (message.type === InteractionType.PING) {
5456
return new JSONResponse({
5557
type: InteractionResponseType.PONG,
@@ -78,4 +80,11 @@ export default {
7880
}
7981
return router.handle(request, env);
8082
},
83+
84+
//keeping these code under comment until we patch the pagination
85+
//of overdue tasks API
86+
87+
// async scheduled(req: Request, env: env, ctx: ExecutionContext) {
88+
// ctx.waitUntil(send(env));
89+
// },
8190
};

src/register.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ async function registerGuildCommands(
1717
discordApplicationId?: string,
1818
discordGuildId?: string
1919
) {
20-
const commands = [HELLO, VERIFY, MENTION_EACH];
20+
const commands = [HELLO, VERIFY, MENTION_EACH, LISTENING];
2121

2222
try {
2323
if (!discordBotToken) throw new Error("Please provide a BOT TOKEN");
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
export interface TaskOverdue {
2+
id: string;
3+
title: string;
4+
type: string;
5+
endsOn: string;
6+
startedOn: string;
7+
status: string;
8+
assignee?: string;
9+
assigneeId: string;
10+
percentCompleted: number;
11+
dependsOn: string[];
12+
participants?: string[];
13+
isNoteworthy: boolean;
14+
createdBy: string;
15+
}
16+
17+
export interface TaskOverdueResponse {
18+
message: string;
19+
tasks: TaskOverdue[];
20+
next?: string;
21+
prev?: string;
22+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
export interface UserBackend {
2+
message: string;
3+
user: {
4+
id: string;
5+
username: string;
6+
first_name: string;
7+
last_name: string;
8+
github_id: string;
9+
discordId: string;
10+
github_display_name: string;
11+
isMember: boolean;
12+
};
13+
}

src/utils/getDiscordIds.ts

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
import { RDS_BASE_API_URL } from "../constants/urls";
2+
import { UserBackend } from "../typeDefinitions/userBackend.types";
3+
import * as response from "../constants/responses";
4+
5+
export const getDiscordIds = async (
6+
userIds: string[] | string
7+
): Promise<string[] | string> => {
8+
try {
9+
const url = `${RDS_BASE_API_URL}/users/userId`;
10+
11+
const numberOfBatches = Math.ceil(userIds.length / 6);
12+
13+
const fetchCollection = [] as Array<Promise<Response>[]>;
14+
15+
//for storing the data returned
16+
const fetchResponseCollection = [] as Array<UserBackend[]>;
17+
18+
let indexCollection = 0;
19+
let numberOfFetchCalls = 0;
20+
21+
const discordIds = [] as Array<string>;
22+
23+
//adding all batch
24+
for (let i = 0; i < numberOfBatches; i++) {
25+
const batch: Promise<Response>[] = [];
26+
fetchCollection.push(batch);
27+
}
28+
29+
//CF workers only allow 6 outgoing simultaneous request, so in order to retrieve all the data
30+
//we are making fetch calls in the batches of 6
31+
32+
for (let i = 0; i < userIds.length; i++) {
33+
fetchCollection[indexCollection].push(fetch(`${url}/${userIds[i]}`));
34+
numberOfFetchCalls++;
35+
36+
if (numberOfFetchCalls == 6) {
37+
indexCollection++;
38+
numberOfFetchCalls = 0;
39+
}
40+
}
41+
42+
//now we will make parallel requests in the batches of 6.
43+
//and extract the data in response array
44+
45+
for (let i = 0; i < numberOfBatches; i++) {
46+
const responses = await Promise.all(fetchCollection[i]);
47+
const json: Promise<UserBackend>[] = responses.map((response) =>
48+
response.json()
49+
);
50+
const data = await Promise.all(json);
51+
52+
fetchResponseCollection.push(data);
53+
}
54+
55+
//data contains arrays of User objects
56+
//we are looping over nested arrays and extracting discordIds
57+
58+
fetchResponseCollection.forEach((dataArray: UserBackend[]) => {
59+
dataArray.forEach((data: UserBackend) => {
60+
if (data.user.discordId) {
61+
discordIds.push(data.user.discordId);
62+
}
63+
});
64+
});
65+
66+
return discordIds;
67+
} catch (e) {
68+
return response.INTERNAL_SERVER_ERROR;
69+
}
70+
};
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { RDS_BASE_API_URL } from "../constants/urls";
2+
import {
3+
TaskOverdue,
4+
TaskOverdueResponse,
5+
} from "../typeDefinitions/taskOverdue.types";
6+
import * as errors from "../constants/responses";
7+
8+
export const taskOverDueDiscordMembers = async (): Promise<
9+
string[] | string
10+
> => {
11+
try {
12+
const overDueUrl = `${RDS_BASE_API_URL}/tasks?dev=true&status=overdue&size=100`;
13+
14+
const response: Response = await fetch(overDueUrl);
15+
const responseObj: TaskOverdueResponse = await response.json();
16+
17+
const assigneeIds: string[] = responseObj.tasks.map(
18+
(task: TaskOverdue) => task.assigneeId
19+
);
20+
21+
return assigneeIds;
22+
} catch (e) {
23+
return errors.INTERNAL_SERVER_ERROR;
24+
}
25+
};

0 commit comments

Comments
 (0)