Skip to content

Commit 32b3cbc

Browse files
authored
FEATURE : /notify command (RealDevSquad#138)
* FEATURE: add code chages for notify command * REFACTOR: removed duplicate types and refactor * REFACTOR: add new types for user and refactor * FIX: add new function to get dicord ID from response and fix response message * REFACTOR: added choices for command type * REFACTOR: create constants for message * ENHANCEMENT: added util function doc comments * ENHANCEMENT: added util function doc comments * TEST: test and mock data added for utils functions * TYPO: removed console from index file * Update commands.ts
1 parent 45f7317 commit 32b3cbc

File tree

13 files changed

+511
-2
lines changed

13 files changed

+511
-2
lines changed

src/constants/commands.ts

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,3 +79,50 @@ export const USER = {
7979
},
8080
],
8181
};
82+
83+
export const NOTIFY = {
84+
name: "notify",
85+
description: "notify the user",
86+
options: [
87+
{
88+
name: "type",
89+
description: "type of notification",
90+
type: 3,
91+
required: true,
92+
choices: [
93+
{
94+
name: "OVERDUE",
95+
value: "OVERDUE",
96+
},
97+
{
98+
name: "ONBOARDING",
99+
value: "ONBOARDING",
100+
},
101+
],
102+
},
103+
{
104+
name: "sub-type",
105+
description: "sub-type of notification",
106+
type: 3,
107+
required: false,
108+
choices: [
109+
{
110+
name: "In 1 Day",
111+
value: "1",
112+
},
113+
{
114+
name: "In 2 Day",
115+
value: "2",
116+
},
117+
{
118+
name: "> 7 Days",
119+
value: "7",
120+
},
121+
{
122+
name: "> 31 Days",
123+
value: "31",
124+
},
125+
],
126+
},
127+
],
128+
};

src/constants/responses.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,3 +55,11 @@ export const TASKS_FETCH_FAILED = "An error occurred while fetching tasks.";
5555
export const FAILED_TO_FETCH_TASKS = `Failed to fetch tasks for **{{assignee}}**.`;
5656
export const USER_NOT_FOUND = `User Not Found`;
5757
export const USER_STATUS_NOT_FOUND = "No Status Found";
58+
59+
export const OVERDUE_DEFAULT_MESSAGE = "You have overdue tasks.";
60+
export const OVERDUE_CUSTOM_MESSAGE =
61+
"Please be aware that you currently have tasks that are overdue or due within the next {{days}} day. If you require additional time to complete these tasks, kindly submit an extension request.";
62+
63+
export const ONBOARDING_DEFAULT_MESSAGE = `You currently have an onboarding status. Please provide an update explaining any challenges you're facing in completing your tasks. If you're finished, consider assigning new tasks to Admin.`;
64+
65+
export const ONBOARDING_CUSTOM_MESSAGE = `Please update your status explaining why you are unable to complete your onboarding tasks within {{days}} days.`;

src/controllers/baseHandler.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { helloCommand } from "./helloCommand";
33
import { verifyCommand } from "./verifyCommand";
44
import { mentionEachUser } from "./mentionEachUser";
55
import { taskCommand } from "./taskCommand";
6+
import { notifyCommand } from "./notifyCommand";
67
import { oooCommand } from "./oooCommand";
78
import { userCommand } from "./userCommand";
89

@@ -22,6 +23,7 @@ import {
2223
MENTION_EACH,
2324
VERIFY,
2425
TASK,
26+
NOTIFY,
2527
OOO,
2628
USER,
2729
} from "../constants/commands";
@@ -118,7 +120,11 @@ export async function baseHandler(
118120
}
119121
case getCommandName(TASK): {
120122
const data = message.data?.options as Array<messageRequestDataOptions>;
121-
return await taskCommand(data[0].value, env);
123+
return await taskCommand(data[0].value);
124+
}
125+
case getCommandName(NOTIFY): {
126+
const data = message.data?.options as Array<messageRequestDataOptions>;
127+
return await notifyCommand(data);
122128
}
123129
case getCommandName(OOO): {
124130
const data = message.data?.options as Array<messageRequestDataOptions>;

src/controllers/notifyCommand.ts

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
import {
2+
OVERDUE_DEFAULT_MESSAGE,
3+
OVERDUE_CUSTOM_MESSAGE,
4+
ONBOARDING_DEFAULT_MESSAGE,
5+
ONBOARDING_CUSTOM_MESSAGE,
6+
} from "../constants/responses";
7+
import {
8+
UserOverdueTaskResponseType,
9+
UserResponseType,
10+
} from "../typeDefinitions/rdsUser";
11+
import { discordTextResponse } from "../utils/discordResponse";
12+
import { fetchRdsData } from "../utils/fetchRdsData";
13+
import { createTaggableDiscordIds } from "../utils/createTaggableDiscordIds";
14+
import { extractDiscordIds } from "../utils/extractDiscordIds";
15+
16+
export async function notifyCommand(data: Array<{ value: string }>) {
17+
const typeValue = data[0].value;
18+
const daysValue = data[1]?.value;
19+
20+
try {
21+
if (typeValue === "OVERDUE") {
22+
const options = {
23+
isOverdue: true,
24+
days: daysValue,
25+
};
26+
const usersResponse = (await fetchRdsData(
27+
options
28+
)) as UserOverdueTaskResponseType;
29+
const discordIDs = extractDiscordIds(usersResponse);
30+
const formattedIds = createTaggableDiscordIds(discordIDs);
31+
32+
const message = `**Message:** ${
33+
daysValue
34+
? OVERDUE_CUSTOM_MESSAGE.replace("{{days}}", daysValue)
35+
: OVERDUE_DEFAULT_MESSAGE
36+
}`;
37+
38+
const users = `**Developers:** ${formattedIds.join(", ")}`;
39+
const responseMessage = `${message}\n${users}`;
40+
return discordTextResponse(responseMessage);
41+
} else if (typeValue === "ONBOARDING") {
42+
const options = {
43+
isOnboarding: true,
44+
days: daysValue,
45+
};
46+
const users = (await fetchRdsData(options)) as UserResponseType;
47+
const discordIDs = extractDiscordIds(users);
48+
const formattedIds = createTaggableDiscordIds(discordIDs);
49+
50+
const message = `**Message:** ${
51+
daysValue
52+
? ONBOARDING_CUSTOM_MESSAGE.replace("{{days}}", daysValue)
53+
: ONBOARDING_DEFAULT_MESSAGE
54+
}`;
55+
56+
const usersMessage = `**Developers:** ${formattedIds.join(", ")}`;
57+
const responseMessage = `${message}\n${usersMessage}`;
58+
return discordTextResponse(responseMessage);
59+
}
60+
const responseMessage = "Please provide a valid type";
61+
return discordTextResponse(responseMessage);
62+
} catch (error) {
63+
console.error(error);
64+
return discordTextResponse("Something went wrong");
65+
}
66+
}

src/register.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import {
44
VERIFY,
55
LISTENING,
66
TASK,
7+
NOTIFY,
78
OOO,
89
USER,
910
} from "./constants/commands";
@@ -25,7 +26,16 @@ async function registerGuildCommands(
2526
discordApplicationId?: string,
2627
discordGuildId?: string
2728
) {
28-
const commands = [HELLO, VERIFY, MENTION_EACH, LISTENING, TASK, OOO, USER];
29+
const commands = [
30+
HELLO,
31+
VERIFY,
32+
MENTION_EACH,
33+
LISTENING,
34+
TASK,
35+
OOO,
36+
USER,
37+
NOTIFY,
38+
];
2939

3040
try {
3141
if (!discordBotToken) throw new Error("Please provide a BOT TOKEN");

src/typeDefinitions/rdsUser.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { task } from "./task.types";
12
export type UserType = {
23
id: string;
34
profileURL?: string;
@@ -43,3 +44,16 @@ export type UserListResponseType = {
4344
count: number;
4445
users: string[];
4546
};
47+
48+
export type UserOverdueTask = {
49+
id: string;
50+
discordId: string;
51+
username: string;
52+
tasks?: task[];
53+
};
54+
55+
export type UserOverdueTaskResponseType = {
56+
message: string;
57+
count: number;
58+
users: UserOverdueTask[];
59+
};
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
/**
2+
* Creates a taggable discord id from a list of discord ids
3+
* @param discordIds - List of discord ids to be formatted eg: ["123", "456", "789"]
4+
* @returns A list of taggable discord ids eg: ["<@123>", "<@456>", "<@789>"]
5+
*/
6+
export function createTaggableDiscordIds(discordIds: string[]) {
7+
const formattedIds = discordIds.map((id) => `<@${id}>`);
8+
return formattedIds;
9+
}

src/utils/extractDiscordIds.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import {
2+
UserOverdueTaskResponseType,
3+
UserResponseType,
4+
UserOverdueTask,
5+
UserType,
6+
} from "../typeDefinitions/rdsUser";
7+
8+
/**
9+
* Extracts discord IDs from a UserResponseType or UserOverdueTaskResponseType which fetches data from the RDS API
10+
* @param usersResponse - A UserResponseType or UserOverdueTaskResponseType
11+
* @returns A list of discord IDs eg: ["123", "456", "789"]
12+
*/
13+
14+
export function extractDiscordIds(
15+
usersResponse: UserOverdueTaskResponseType | UserResponseType
16+
): string[] {
17+
const userData = usersResponse?.users;
18+
const discordIDs: string[] = [];
19+
userData?.forEach((user: UserOverdueTask | UserType) => {
20+
const discordId = user?.discordId;
21+
if (discordId) {
22+
discordIDs.push(discordId);
23+
}
24+
});
25+
return discordIDs;
26+
}

src/utils/fetchRdsData.ts

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import { RDS_BASE_API_URL } from "../constants/urls";
2+
import {
3+
UserResponseType,
4+
UserOverdueTaskResponseType,
5+
} from "../typeDefinitions/rdsUser";
6+
7+
/**
8+
* Fetches user(s) from RDS API based on the options provided
9+
* @param {Object} options - Options object
10+
* @param {string} options.days - Number of days
11+
* @param {boolean} options.isOnboarding - Whether to fetch onboarding users
12+
* @param {boolean} options.isOverdue - Whether to fetch overdue task users
13+
* @returns {Promise<UserResponseType | UserOverdueTaskResponseType>} - User(s) response
14+
* @throws {Error} - Error object
15+
*/
16+
17+
type OptionsType = {
18+
days?: string;
19+
isOnboarding?: boolean;
20+
isOverdue?: boolean;
21+
};
22+
23+
async function fetchRdsData(options = {}) {
24+
try {
25+
const { days, isOnboarding, isOverdue }: OptionsType = options;
26+
let url = RDS_BASE_API_URL;
27+
28+
if (isOnboarding) {
29+
url += days
30+
? `/users/search?state=ONBOARDING&time=${days}d`
31+
: `/users/search?state=ONBOARDING`;
32+
} else if (isOverdue) {
33+
url += days
34+
? `/users?query=filterBy:overdue_tasks+days:${days}`
35+
: `/users?query=filterBy:overdue_tasks`;
36+
}
37+
const response = await fetch(url);
38+
39+
if (isOnboarding) {
40+
const responseData: UserResponseType = await response.json();
41+
return responseData;
42+
} else if (isOverdue) {
43+
const responseData: UserOverdueTaskResponseType = await response.json();
44+
return responseData;
45+
}
46+
} catch (error) {
47+
console.error("An error occurred while fetching user(s):", error);
48+
throw error;
49+
}
50+
}
51+
52+
export { fetchRdsData };

0 commit comments

Comments
 (0)