Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dev to main sync #221

Merged
merged 35 commits into from
Apr 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
39c5815
feat: add task update handler and util
tejaskh3 Mar 27, 2024
b48fa22
feat: app api to send task updates on discord
tejaskh3 Apr 4, 2024
6edbac3
feat:add test case for util function
tejaskh3 Apr 5, 2024
83b19f2
Merge branch 'develop' into dc-update
tejaskh3 Apr 5, 2024
d494fe6
fix: lint issues
tejaskh3 Apr 5, 2024
f4d3d0b
feat: add test cases
tejaskh3 Apr 5, 2024
d6cd583
fix: lint issues
tejaskh3 Apr 5, 2024
c9f63cd
fix: try-catch block
tejaskh3 Apr 6, 2024
a5010fb
added feature to remove tagged users of a particular role
rishirishhh Apr 7, 2024
ed1a35f
fix: status code of responses
tejaskh3 Apr 8, 2024
b2f082b
fix: status code of responses
tejaskh3 Apr 8, 2024
0b2b573
refactor: add types of task update
tejaskh3 Apr 8, 2024
d3c78da
fix: status code of responses
tejaskh3 Apr 8, 2024
38141ec
refactor:add req type
tejaskh3 Apr 10, 2024
01c526d
added new changes in test
rishirishhh Apr 11, 2024
6fac2a0
refactor: change API endpoint and related logic to get overdue task u…
sahsisunny Apr 13, 2024
516bb00
test: change test fixtures data and expect logic based on previous co…
sahsisunny Apr 13, 2024
9a116d2
NPM Script : Introduce new command for automated code style correctio…
sahsisunny Apr 13, 2024
e9f6aff
feat: add test cases
tejaskh3 Apr 13, 2024
bee81df
refactor: add a assert function
tejaskh3 Apr 13, 2024
b59672e
Merge branch 'develop' into dc-update
iamitprakash Apr 13, 2024
f425132
made changes in the code as per reviews
rishirishhh Apr 13, 2024
8bf739b
Merge branch 'develop' into feat/remove-tagged-users
rishirishhh Apr 13, 2024
dd6902b
refactor: response messgage and add try-catch
tejaskh3 Apr 13, 2024
d8c8414
Merge branch 'dc-update' of https://github.com/Real-Dev-Squad/discord…
tejaskh3 Apr 13, 2024
2dc5502
Update baseHandler.ts
rishirishhh Apr 14, 2024
dd9624e
improved the removeUsers functionality
rishirishhh Apr 14, 2024
bab55b8
Merge branch 'develop' into feat/remove-tagged-users
rishirishhh Apr 14, 2024
53f1444
Merge branch 'feat/remove-tagged-users' of https://github.com/rishiri…
rishirishhh Apr 14, 2024
ac2507c
Merge pull request #218 from rishirishhh/feat/remove-tagged-users
iamitprakash Apr 15, 2024
3d82b14
Merge pull request #213 from Real-Dev-Squad/dc-update
iamitprakash Apr 15, 2024
30150ac
Merge branch 'develop' into issue-219-online-users-filter
sahsisunny Apr 15, 2024
b0f91cd
refactor: refactor and filtred discord ids
sahsisunny Apr 16, 2024
bdef426
Merge pull request #220 from Real-Dev-Squad/issue-219-online-users-fi…
iamitprakash Apr 17, 2024
4aec478
Merge branch 'main' into develop
iamitprakash Apr 18, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
"lint-fix": "eslint --fix .",
"format-check": "echo 'Checking the formatting of your code 👨‍💻' && prettier --check . && echo '✅ code matches prettier formatting'",
"format-fix": "prettier --write .",
"fix": "npm run lint-fix && npm run format-fix",
"ngrok": "ngrok http 8787",
"register": "ts-node-esm src/register.ts"
},
Expand Down
13 changes: 13 additions & 0 deletions src/constants/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,19 @@ export const MENTION_EACH = {
],
};

export const REMOVE = {
name: "remove",
description: "remove user/users from the server",
options: [
{
name: "role",
description: "remove developers with specific role",
type: 8, // User type
required: true,
},
],
};

export const LISTENING = {
name: "listening",
description: "mark user as listening",
Expand Down
11 changes: 11 additions & 0 deletions src/controllers/baseHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import {
NOTIFY_ONBOARDING,
OOO,
USER,
REMOVE,
} from "../constants/commands";
import { updateNickName } from "../utils/updateNickname";
import { discordEphemeralResponse } from "../utils/discordEphemeralResponse";
Expand All @@ -40,6 +41,7 @@ import {
RETRY_COMMAND,
} from "../constants/responses";
import { DevFlag } from "../typeDefinitions/filterUsersByRole";
import { kickEachUser } from "./kickEachUser";

export async function baseHandler(
message: discordMessageRequest,
Expand Down Expand Up @@ -75,6 +77,15 @@ export async function baseHandler(
return await mentionEachUser(transformedArgument, env, ctx);
}

case getCommandName(REMOVE): {
const data = message.data?.options as Array<messageRequestDataOptions>;
const transformedArgument = {
roleToBeRemovedObj: data[0],
channelId: message.channel_id,
};
return await kickEachUser(transformedArgument, env, ctx);
}

case getCommandName(LISTENING): {
const data = message.data?.options;
const setter = data ? data[0].value : false;
Expand Down
44 changes: 44 additions & 0 deletions src/controllers/kickEachUser.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import {
MentionEachUserOptions,
UserArray,
} from "../typeDefinitions/filterUsersByRole";
import { env } from "../typeDefinitions/default.types";
import { getMembersInServer } from "../utils/getMembersInServer";
import { filterUserByRoles } from "../utils/filterUsersByRole";
import { discordTextResponse } from "../utils/discordResponse";
import { removeUsers } from "../utils/removeUsers";

export async function kickEachUser(
transformedArgument: {
roleToBeRemovedObj: MentionEachUserOptions;
channelId: number;
},
env: env,
ctx: ExecutionContext
) {
const getMembersInServerResponse = await getMembersInServer(env);
const roleId = transformedArgument.roleToBeRemovedObj.value;

const usersWithMatchingRole = filterUserByRoles(
getMembersInServerResponse as UserArray[],
roleId
);

const usersText = usersWithMatchingRole
.map((user) => {
return user;
})
.join("\n");

if (usersWithMatchingRole.length === 0) {
return discordTextResponse(
`We couldn't find any user(s) assigned to <@&${roleId}> role.`
);
} else {
ctx.waitUntil(
removeUsers(env, usersWithMatchingRole, transformedArgument.channelId)
);
const responseText = `Following users will be removed shortly ..... :\n${usersText}`;
return discordTextResponse(responseText);
}
}
29 changes: 29 additions & 0 deletions src/controllers/taskUpdatesHandler.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { env } from "../typeDefinitions/default.types";
import JSONResponse from "../utils/JsonResponse";
import * as response from "../constants/responses";
import { verifyNodejsBackendAuthToken } from "../utils/verifyAuthToken";
import { sendTaskUpdate } from "../utils/sendTaskUpdates";
import { TaskUpdates } from "../typeDefinitions/taskUpdate";
import { IRequest } from "itty-router";

export const sendTaskUpdatesHandler = async (request: IRequest, env: env) => {
const authHeader = request.headers.get("Authorization");
if (!authHeader) {
return new JSONResponse(response.UNAUTHORIZED, { status: 401 });
}
try {
await verifyNodejsBackendAuthToken(authHeader, env);
const updates: TaskUpdates = await request.json();
const { completed, planned, blockers } = updates.content;
await sendTaskUpdate(completed, planned, blockers, env);
return new JSONResponse(
"Task update sent on Discord's tracking-updates channel."
);
} catch (error: any) {
return new JSONResponse({
res: response.INTERNAL_SERVER_ERROR,
message: error.message,
status: 500,
});
}
};
20 changes: 11 additions & 9 deletions src/handlers/scheduledEventHandler.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,26 @@
import { env } from "../typeDefinitions/default.types";
import { taskOverDueDiscordMembers } from "../utils/taskOverDueDiscordMembers";
import * as error from "../constants/responses";
import { getDiscordIds } from "../utils/getDiscordIds";
import config from "../../config/config";
import { SUPER_USER_ONE, SUPER_USER_TWO } from "../constants/variables";

export async function send(env: env): Promise<void> {
try {
const assigneeIds: string[] | string = await taskOverDueDiscordMembers();
const discordIds: string[] = await taskOverDueDiscordMembers();

//A user might have more than one task which are running red
//so to mention them just once, we are using Set to filter out
const discordIds: string[] | string = await getDiscordIds(assigneeIds);
const uniqueDiscordIds = [...new Set(discordIds)];
const superUsers = [SUPER_USER_ONE, SUPER_USER_TWO];
const filteredDiscordIds = discordIds.filter(
(id) => !superUsers.includes(id)
);

if (filteredDiscordIds.length === 0) {
return;
}

//notifying the two users with the authority.
let stringToBeSent = `<@${SUPER_USER_ONE}> <@${SUPER_USER_TWO}>\nThese people have their task running red:\n`;

let forFormatting = 0;
uniqueDiscordIds.forEach((id) => {
filteredDiscordIds.forEach((id: string) => {
const discordUser = `<@${id}> `;
stringToBeSent += discordUser;
forFormatting++;
Expand All @@ -35,7 +37,7 @@ export async function send(env: env): Promise<void> {

const url = config(env).TRACKING_CHANNEL_URL;

const res = await fetch(url, {
await fetch(url, {
method: "POST",
body: JSON.stringify(bodyObj),
headers: {
Expand Down
5 changes: 4 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { getGuildMemberDetailsHandler } from "./controllers/getGuildMemberDetail
import { send } from "./handlers/scheduledEventHandler";
import { generateInviteLink } from "./controllers/generateDiscordInvite";
import { sendProfileBlockedMessage } from "./controllers/profileHandler";
import { sendTaskUpdatesHandler } from "./controllers/taskUpdatesHandler";

const router = Router();

Expand Down Expand Up @@ -57,6 +58,8 @@ router.delete("/roles", removeGuildRoleHandler);

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

router.post("/task/update", sendTaskUpdatesHandler);

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

Expand All @@ -83,7 +86,7 @@ export default {
env: env,
ctx: ExecutionContext
): Promise<Response> {
const apiUrls = ["/invite", "/roles", "/profile/blocked"];
const apiUrls = ["/invite", "/roles", "/profile/blocked", "/task/update"];
const url = new URL(request.url);
if (request.method === "POST" && !apiUrls.includes(url.pathname)) {
const isVerifiedRequest = await verifyBot(request, env);
Expand Down
2 changes: 2 additions & 0 deletions src/register.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
NOTIFY_ONBOARDING,
OOO,
USER,
REMOVE,
} from "./constants/commands";
import { config } from "dotenv";
import { DISCORD_BASE_URL } from "./constants/urls";
Expand Down Expand Up @@ -37,6 +38,7 @@ async function registerGuildCommands(
USER,
NOTIFY_OVERDUE,
NOTIFY_ONBOARDING,
REMOVE,
];

try {
Expand Down
7 changes: 7 additions & 0 deletions src/typeDefinitions/taskUpdate.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export interface TaskUpdates {
content: {
completed: string;
planned: string;
blockers: string;
};
}
76 changes: 76 additions & 0 deletions src/utils/removeUsers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import { env } from "../typeDefinitions/default.types";
import { DISCORD_BASE_URL } from "../constants/urls";
import {
parseRateLimitRemaining,
parseResetAfter,
} from "./batchDiscordRequests";
import {
discordMessageRequest,
discordMessageError,
} from "../typeDefinitions/discordMessage.types";

export async function removeUsers(
env: env,
usersWithMatchingRole: string[],
channelId: number
) {
const batchSize = 4;
let waitTillNextAPICall = 0;

try {
const failedUsers: Array<string> = [];
for (let i = 0; i < usersWithMatchingRole.length; i += batchSize) {
const batchwiseUsers = usersWithMatchingRole.slice(i, i + batchSize);
const deleteRequests = batchwiseUsers.map((mention) => {
const userId = mention.replace(/<@!*/g, "").replace(/>/g, "");
const url = `${DISCORD_BASE_URL}/guilds/${env.DISCORD_GUILD_ID}/members/${userId}`;

return fetch(url, {
method: "DELETE",
headers: {
"Content-Type": "application/json",
Authorization: `Bot ${env.DISCORD_TOKEN}`,
},
}).then((response) => {
const rateLimitRemaining = parseRateLimitRemaining(response);
if (rateLimitRemaining === 0) {
waitTillNextAPICall = Math.max(
parseResetAfter(response),
waitTillNextAPICall
);
}
return response.json();
}) as Promise<discordMessageRequest | discordMessageError>;
});

const responses = await Promise.all(deleteRequests);
responses.forEach((response, i) => {
if (response && "message" in response) {
failedUsers.push(batchwiseUsers[i]);
console.error(`Failed to remove a user`);
}
});
await sleep(waitTillNextAPICall * 1000);
waitTillNextAPICall = 0;
}

if (failedUsers.length > 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 remove ${failedUsers}.`,
}),
});
}
} catch (error) {
console.error("Error occurred while removing users:", error);
}
}

function sleep(ms: number) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
34 changes: 34 additions & 0 deletions src/utils/sendTaskUpdates.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { env } from "../typeDefinitions/default.types";
import config from "../../config/config";

export async function sendTaskUpdate(
completed: string,
planned: string,
blockers: string,
env: env
): Promise<void> {
const formattedString = `**Completed**: ${completed}\n\n**Planned**: ${planned}\n\n**Blockers**: ${blockers}`;
const bodyObj = {
content: formattedString,
};
const url = config(env).TRACKING_CHANNEL_URL;
try {
const response = await fetch(url, {
method: "POST",
body: JSON.stringify(bodyObj),
headers: {
"Content-Type": "application/json",
Authorization: `Bot ${env.DISCORD_TOKEN}`,
},
});

if (!response.ok) {
throw new Error(
`Failed to send task update: ${response.status} - ${response.statusText}`
);
}
} catch (error) {
console.error("Error occurred while sending task update:", error);
throw error;
}
}
20 changes: 7 additions & 13 deletions src/utils/taskOverDueDiscordMembers.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,18 @@
import { RDS_BASE_API_URL } from "../constants/urls";
import {
TaskOverdue,
TaskOverdueResponse,
} from "../typeDefinitions/taskOverdue.types";
import * as errors from "../constants/responses";
import { UserOverdueTaskResponseType } from "../typeDefinitions/rdsUser";

export const taskOverDueDiscordMembers = async (): Promise<
string[] | string
> => {
export const taskOverDueDiscordMembers = async (): Promise<string[]> => {
try {
const overDueUrl = `${RDS_BASE_API_URL}/tasks?dev=true&status=overdue&size=100`;
const overDueUrl = `${RDS_BASE_API_URL}/users?query=filterBy:overdue_tasks`;

const response: Response = await fetch(overDueUrl);
const responseObj: TaskOverdueResponse = await response.json();
const responseObj: UserOverdueTaskResponseType = await response.json();

const assigneeIds: string[] = responseObj.tasks.map(
(task: TaskOverdue) => task.assigneeId
const discordIds: string[] = responseObj.users.map(
(user) => user.discordId
);

return assigneeIds;
return discordIds;
} catch (e) {
console.log(e);
throw e;
Expand Down
Loading
Loading