Skip to content

Commit 9de6ce0

Browse files
feat: mention each command with series support (#69)
* feat: registering commands * feat: add commands * feat: add types for command * feat: adding logic for mention each * feat: mention each logic to respond with users who have role mentioned in the message * feat: adding types because i have to :O * feat: sweet logic which returns me the users who have roles mentioned in the command * feat: changed the input args for the function * feat: tested with cases * feat: tested with cases * refact: imports and some beauties XD * created: types file cause had to * nuked: cause renamed ito * feat: common who doesnt like smaller util functions? * refact: imports * chore: to handle pre hooks * chore: annoying linter * chore: annoying linter * refactor: resolving PR comments * refactor: sorting imports as well as fix for pr comments * chore: because lint decided to be pain in my code * chore: resolving comments for PR * chore: XD
1 parent 67342e1 commit 9de6ce0

File tree

10 files changed

+279
-8
lines changed

10 files changed

+279
-8
lines changed

src/constants/commands.ts

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,63 @@ export const VERIFY = {
88
description:
99
"Generate a link with user specific token to link with RDS backend.",
1010
};
11+
12+
export const MENTION_EACH = {
13+
name: "mention",
14+
description: "mention each user with this role",
15+
options: [
16+
{
17+
name: "list",
18+
description: "get user in list",
19+
type: 2, // 2 is type SUB_COMMAND_GROUP
20+
options: [
21+
{
22+
name: "get",
23+
description: "get me the role ",
24+
type: 1, // 1 is type SUB_COMMAND
25+
options: [
26+
{
27+
name: "user",
28+
description: "The user to get",
29+
type: 9, // 6 is type USER
30+
required: true,
31+
},
32+
{
33+
name: "message",
34+
description: "What message to send to user ?",
35+
type: 3, // 7 is type CHANNEL
36+
required: true,
37+
},
38+
],
39+
},
40+
],
41+
},
42+
{
43+
name: "series",
44+
description: "get user in series",
45+
type: 2,
46+
options: [
47+
{
48+
name: "get",
49+
description: "get me the role",
50+
type: 1,
51+
options: [
52+
{
53+
name: "user",
54+
description: "The user to get",
55+
type: 9, // 8 is type ROLE
56+
required: true,
57+
},
58+
{
59+
name: "message",
60+
description:
61+
"The channel permissions to get. If omitted, the guild permissions will be returned",
62+
type: 3,
63+
required: true,
64+
},
65+
],
66+
},
67+
],
68+
},
69+
],
70+
};

src/controllers/baseHandler.ts

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,26 @@
1-
import { HELLO, VERIFY } from "../constants/commands";
2-
import { env } from "../typeDefinitions/default.types";
3-
import { discordMessageRequest } from "../typeDefinitions/discordMessage.types";
4-
import { getCommandName } from "../utils/getCommandName";
5-
import JSONResponse from "../utils/JsonResponse";
6-
import { lowerCaseMessageCommands } from "../utils/lowerCaseMessageCommand";
71
import { commandNotFound } from "./commandNotFound";
82
import { helloCommand } from "./helloCommand";
93
import { verifyCommand } from "./verifyCommand";
4+
import { mentionEachUser } from "./mentionEachUser";
5+
6+
import { getCommandName } from "../utils/getCommandName";
7+
import JSONResponse from "../utils/JsonResponse";
8+
import { lowerCaseMessageCommands } from "../utils/lowerCaseMessageCommand";
9+
10+
import { env } from "../typeDefinitions/default.types";
11+
import {
12+
discordMessageRequest,
13+
messageRequestDataOptions,
14+
} from "../typeDefinitions/discordMessage.types";
15+
16+
import { HELLO, MENTION_EACH, VERIFY } from "../constants/commands";
1017

1118
export async function baseHandler(
1219
message: discordMessageRequest,
1320
env: env
1421
): Promise<JSONResponse> {
1522
const command = lowerCaseMessageCommands(message);
23+
1624
switch (command) {
1725
case getCommandName(HELLO): {
1826
return helloCommand(message.member.user.id);
@@ -26,6 +34,17 @@ export async function baseHandler(
2634
env
2735
);
2836
}
37+
case getCommandName(MENTION_EACH): {
38+
const data = message.data?.options as Array<messageRequestDataOptions>;
39+
40+
return await mentionEachUser(
41+
{
42+
displayType: data[0].name,
43+
options: data[0].options[0].options,
44+
},
45+
env
46+
);
47+
}
2948
default: {
3049
return commandNotFound();
3150
}

src/controllers/mentionEachUser.ts

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import { discordTextResponse } from "../utils/discordResponse";
2+
import { filterUserByRoles } from "../utils/filterUsersByRole";
3+
import { getMembersInServer } from "../utils/getMembersInServer";
4+
5+
import { env } from "../typeDefinitions/default.types";
6+
import {
7+
UserArray,
8+
MentionEachUserOptions,
9+
} from "../typeDefinitions/filterUsersByRole";
10+
import { checkDisplayType } from "../utils/checkDisplayType";
11+
12+
export async function mentionEachUser(
13+
message: { displayType: string; options: Array<MentionEachUserOptions> },
14+
env: env
15+
) {
16+
const getMembersInServerResponse = await getMembersInServer(env);
17+
18+
// displaytype is list or series & options is first level of options array on desctructure
19+
const { displayType, options } = message;
20+
const [roleToBeTaggedObj, displayMessageObj] = options;
21+
22+
const roleId = roleToBeTaggedObj.value;
23+
const msgToBeSent = displayMessageObj.value;
24+
25+
const usersWithMatchingRole = filterUserByRoles(
26+
getMembersInServerResponse as UserArray[],
27+
roleId
28+
);
29+
30+
const responseData = checkDisplayType({
31+
displayType,
32+
msgToBeSent,
33+
usersWithMatchingRole,
34+
});
35+
return discordTextResponse(responseData);
36+
}

src/register.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { HELLO, VERIFY } from "./constants/commands";
1+
import { HELLO, MENTION_EACH, VERIFY } from "./constants/commands";
22
import { config } from "dotenv";
33
import { DISCORD_BASE_URL } from "./constants/urls";
44
import { registerCommands } from "./utils/registerCommands";
@@ -17,7 +17,7 @@ async function registerGuildCommands(
1717
discordApplicationId?: string,
1818
discordGuildId?: string
1919
) {
20-
const commands = [HELLO, VERIFY];
20+
const commands = [HELLO, VERIFY, MENTION_EACH];
2121

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

src/typeDefinitions/discordMessage.types.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ export interface messageRequestDataOptions {
1414
name: string;
1515
type: number;
1616
value: string;
17+
options: Array<messageRequestDataOptions>;
1718
}
1819

1920
export interface messageRequestMember {
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
export type UserArray = {
2+
user: {
3+
id: string;
4+
};
5+
roles: string[];
6+
};
7+
8+
export type MentionEachUserOptions = {
9+
name: string;
10+
type: number;
11+
value: string;
12+
};
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
export interface commandTypes {
22
name: string;
33
description: string;
4+
type?: number;
5+
required?: boolean;
6+
options?: commandTypes[];
47
}

src/utils/checkDisplayType.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
export function checkDisplayType({
2+
displayType,
3+
msgToBeSent,
4+
usersWithMatchingRole,
5+
}: {
6+
displayType: string;
7+
msgToBeSent: string;
8+
usersWithMatchingRole: string[];
9+
}) {
10+
if (displayType === "list") {
11+
return `Coming soon. We are working on this feature. We feel sorry for not serving you what you expect this command to do for now.(T_T)`;
12+
} else {
13+
if (usersWithMatchingRole.length > 0) {
14+
return `${displayType && msgToBeSent} ${usersWithMatchingRole}`;
15+
} else {
16+
return `Sorry no user found under this role.`;
17+
}
18+
}
19+
}

src/utils/filterUsersByRole.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { UserArray } from "../typeDefinitions/filterUsersByRole";
2+
3+
export function filterUserByRoles(userArray: UserArray[], roleId: string) {
4+
let filteredUser: string[] | [] = [];
5+
6+
if (userArray?.length > 0 && Array.isArray(userArray)) {
7+
filteredUser = userArray?.reduce((acc: string[], current: UserArray) => {
8+
if (current.roles.includes(roleId)) {
9+
const modedTag = `<@${current.user.id}>`;
10+
11+
acc.push(modedTag);
12+
}
13+
14+
return acc;
15+
}, []);
16+
}
17+
18+
return filteredUser;
19+
}
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
import { mentionEachUser } from "../../../src/controllers/mentionEachUser";
2+
import { filterUserByRoles } from "../../../src/utils/filterUsersByRole";
3+
import { checkDisplayType } from "../../../src/utils/checkDisplayType";
4+
5+
describe("Test mention each function", () => {
6+
it("Should be an instance of JSONResponse", () => {
7+
const env = {
8+
BOT_PUBLIC_KEY: "xyz",
9+
DISCORD_GUILD_ID: "123",
10+
DISCORD_TOKEN: "abc",
11+
};
12+
const optionsArray = [
13+
{
14+
name: "user",
15+
type: 9,
16+
value: "860900892193456149",
17+
},
18+
{
19+
name: "message",
20+
type: 3,
21+
value: "hello",
22+
},
23+
];
24+
25+
const response = mentionEachUser(
26+
{ displayType: "series", options: optionsArray },
27+
env
28+
);
29+
expect(response).toBeInstanceOf(Promise);
30+
});
31+
32+
it("should filter users based on role", () => {
33+
const roleId = "860900892193456149";
34+
const optionsArray = [
35+
{
36+
roles: [
37+
"890520255934377985",
38+
"860900892193456149",
39+
"845302148878565406",
40+
],
41+
user: {
42+
id: "282859044593598464",
43+
},
44+
},
45+
{
46+
roles: ["851059823779905548"],
47+
user: {
48+
id: "559426966151757824",
49+
},
50+
},
51+
{
52+
roles: ["860900892193456149"],
53+
user: {
54+
id: "725745030706364447",
55+
},
56+
},
57+
];
58+
const response = filterUserByRoles(optionsArray, roleId);
59+
const expectedResponse = ["<@282859044593598464>", "<@725745030706364447>"];
60+
expect(response).toStrictEqual(expectedResponse);
61+
});
62+
63+
it("should return a message if users are not found (usersWithMatchingRole array is empty)", () => {
64+
const msgToBeSent = "hello";
65+
66+
const response = checkDisplayType({
67+
displayType: "series",
68+
msgToBeSent,
69+
usersWithMatchingRole: [],
70+
});
71+
const expectedResponse = `Sorry no user found under this role.`;
72+
73+
expect(response).toBe(expectedResponse);
74+
});
75+
76+
it("should return message if displayType is list", () => {
77+
const msgToBeSent = "hello";
78+
79+
const response = checkDisplayType({
80+
displayType: "list",
81+
msgToBeSent,
82+
usersWithMatchingRole: [],
83+
});
84+
const expectedResponse = `Coming soon. We are working on this feature. We feel sorry for not serving you what you expect this command to do for now.(T_T)`;
85+
86+
expect(response).toBe(expectedResponse);
87+
});
88+
89+
it("should return a message if users are found (usersWithMatchingRole array is not empty)", () => {
90+
const msgToBeSent = "hello";
91+
92+
const response = checkDisplayType({
93+
displayType: "series",
94+
msgToBeSent,
95+
usersWithMatchingRole: ["<@282859044593598464>", "<@725745030706364447>"],
96+
});
97+
const expectedResponse =
98+
"hello <@282859044593598464>,<@725745030706364447>";
99+
100+
expect(response).toBe(expectedResponse);
101+
});
102+
});

0 commit comments

Comments
 (0)