From 4ab68665800bc19afdbe231b159e03eb212312f0 Mon Sep 17 00:00:00 2001 From: Achintya-Chatterjee Date: Wed, 13 Mar 2024 01:23:44 +0530 Subject: [PATCH 01/11] feat: mute user on /listening command --- src/controllers/baseHandler.ts | 94 +++++++++++++++---- src/typeDefinitions/discordMessage.types.d.ts | 1 + tests/fixtures/fixture.ts | 2 + 3 files changed, 79 insertions(+), 18 deletions(-) diff --git a/src/controllers/baseHandler.ts b/src/controllers/baseHandler.ts index 639a74d8..4028d8cf 100644 --- a/src/controllers/baseHandler.ts +++ b/src/controllers/baseHandler.ts @@ -39,6 +39,69 @@ import { REMOVED_LISTENING_MESSAGE, RETRY_COMMAND, } from "../constants/responses"; +import { DISCORD_BASE_URL } from "../constants/urls"; + +async function muteUser( + userId: string, + guildId: string, + token: string +): Promise { + try { + const response = await fetch( + `${DISCORD_BASE_URL}/guilds/${guildId}/members/${userId}`, + { + method: "PATCH", + headers: { + "Content-Type": "application/json", + Authorization: `Bot ${token}`, + }, + body: JSON.stringify({ + mute: true, + channel_id: null, + }), + } + ); + + if (response.ok) { + console.log("User muted successfully"); + } else { + console.error(`Error: ${response.status} - ${response.statusText}`); + } + } catch (error) { + console.error("Error occurred:", error); + } +} + +async function unmuteUser( + userId: string, + guildId: string, + token: string +): Promise { + try { + const response = await fetch( + `${DISCORD_BASE_URL}/guilds/${guildId}/members/${userId}`, + { + method: "PATCH", + headers: { + "Content-Type": "application/json", + Authorization: `Bot ${token}`, + }, + body: JSON.stringify({ + mute: false, + channel_id: null, + }), + } + ); + + if (response.ok) { + console.log("User unmuted successfully"); + } else { + console.error(`Error: ${response.status} - ${response.statusText}`); + } + } catch (error) { + console.error("Error occurred:", error); + } +} export async function baseHandler( message: discordMessageRequest, @@ -73,6 +136,7 @@ export async function baseHandler( case getCommandName(LISTENING): { const data = message.data?.options; const setter = data ? data[0].value : false; + const memberId = message.member.user.id.toString(); const nickname = removeListening(message.member.nick || ""); let discordEphemeral; let updateNickNameData = ""; @@ -83,31 +147,25 @@ export async function baseHandler( !message.member.nick.includes(NICKNAME_SUFFIX) ) { updateNickNameData = - NICKNAME_PREFIX + message.member.nick + NICKNAME_SUFFIX; + (message.member.nick.endsWith(NICKNAME_SUFFIX) + ? "" + : NICKNAME_PREFIX) + + message.member.nick + + NICKNAME_SUFFIX; discordEphemeral = LISTENING_SUCCESS_MESSAGE; + await muteUser(memberId, message.guild.id, env.DISCORD_TOKEN); } else if (!message.member.nick) { - (updateNickNameData = NICKNAME_PREFIX + "" + NICKNAME_SUFFIX), - (discordEphemeral = LISTENING_SUCCESS_MESSAGE); + updateNickNameData = NICKNAME_PREFIX + NICKNAME_SUFFIX; + discordEphemeral = LISTENING_SUCCESS_MESSAGE; + await muteUser(memberId, message.guild.id, env.DISCORD_TOKEN); } else { updateNickNameData = message.member.nick; discordEphemeral = ALREADY_LISTENING; } - } else if ( - !setter && - !message.member.nick && - message.member.user.username.includes(NICKNAME_SUFFIX) - ) { - updateNickNameData = message.member.user.username + NICKNAME_SUFFIX; - - discordEphemeral = REMOVED_LISTENING_MESSAGE; } else { - if (message.member.nick?.includes(NICKNAME_SUFFIX)) { - updateNickNameData = nickname; - discordEphemeral = REMOVED_LISTENING_MESSAGE; - } else { - updateNickNameData = nickname; - discordEphemeral = NOTHING_CHANGED; - } + updateNickNameData = nickname; + discordEphemeral = REMOVED_LISTENING_MESSAGE; + await unmuteUser(memberId, message.guild.id, env.DISCORD_TOKEN); } await updateNickName( message.member.user.id.toString(), diff --git a/src/typeDefinitions/discordMessage.types.d.ts b/src/typeDefinitions/discordMessage.types.d.ts index 032d1811..3b00a123 100644 --- a/src/typeDefinitions/discordMessage.types.d.ts +++ b/src/typeDefinitions/discordMessage.types.d.ts @@ -1,4 +1,5 @@ export interface discordMessageRequest { + guild: any; type: number; data: messageRequestData; member: messageRequestMember; diff --git a/tests/fixtures/fixture.ts b/tests/fixtures/fixture.ts index 9d31bc8f..06bf9382 100644 --- a/tests/fixtures/fixture.ts +++ b/tests/fixtures/fixture.ts @@ -23,6 +23,7 @@ export const dummyHelloMessage: discordMessageRequest = { }, }, guild_id: 123456, + guild: undefined, }; export const dummyVerifyMessage: discordMessageRequest = { @@ -39,6 +40,7 @@ export const dummyVerifyMessage: discordMessageRequest = { }, }, guild_id: 123456, + guild: undefined, }; export const dummyCreateBody: createNewRole = { From bebca81f3ca968a4ce3b1ad6f098e8bfdad18576 Mon Sep 17 00:00:00 2001 From: Achintya-Chatterjee Date: Wed, 13 Mar 2024 03:21:24 +0530 Subject: [PATCH 02/11] fix:update user's nickname with the appropriate prefix and suffix --- src/controllers/baseHandler.ts | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/src/controllers/baseHandler.ts b/src/controllers/baseHandler.ts index 4028d8cf..f864ca34 100644 --- a/src/controllers/baseHandler.ts +++ b/src/controllers/baseHandler.ts @@ -142,20 +142,9 @@ export async function baseHandler( let updateNickNameData = ""; try { if (setter) { - if ( - message.member.nick && - !message.member.nick.includes(NICKNAME_SUFFIX) - ) { + if (!message.member.nick?.includes(NICKNAME_SUFFIX)) { updateNickNameData = - (message.member.nick.endsWith(NICKNAME_SUFFIX) - ? "" - : NICKNAME_PREFIX) + - message.member.nick + - NICKNAME_SUFFIX; - discordEphemeral = LISTENING_SUCCESS_MESSAGE; - await muteUser(memberId, message.guild.id, env.DISCORD_TOKEN); - } else if (!message.member.nick) { - updateNickNameData = NICKNAME_PREFIX + NICKNAME_SUFFIX; + NICKNAME_PREFIX + message.member.user.username + NICKNAME_SUFFIX; discordEphemeral = LISTENING_SUCCESS_MESSAGE; await muteUser(memberId, message.guild.id, env.DISCORD_TOKEN); } else { From 4b90767bf36ca2654f1337d837ef082623f49f69 Mon Sep 17 00:00:00 2001 From: Achintya-Chatterjee Date: Wed, 13 Mar 2024 04:06:13 +0530 Subject: [PATCH 03/11] refactor: move mute and unmute functions to utils --- src/controllers/baseHandler.ts | 70 +----------------------------- src/utils/userMuteUnmuteActions.ts | 63 +++++++++++++++++++++++++++ 2 files changed, 64 insertions(+), 69 deletions(-) create mode 100644 src/utils/userMuteUnmuteActions.ts diff --git a/src/controllers/baseHandler.ts b/src/controllers/baseHandler.ts index f864ca34..2f43e892 100644 --- a/src/controllers/baseHandler.ts +++ b/src/controllers/baseHandler.ts @@ -6,17 +6,15 @@ import { taskCommand } from "./taskCommand"; import { notifyCommand } from "./notifyCommand"; import { oooCommand } from "./oooCommand"; import { userCommand } from "./userCommand"; - +import { muteUser, unmuteUser } from "../utils/userMuteUnmuteActions"; import { getCommandName } from "../utils/getCommandName"; import JSONResponse from "../utils/JsonResponse"; import { lowerCaseMessageCommands } from "../utils/lowerCaseMessageCommand"; - import { env } from "../typeDefinitions/default.types"; import { discordMessageRequest, messageRequestDataOptions, } from "../typeDefinitions/discordMessage.types"; - import { HELLO, LISTENING, @@ -41,68 +39,6 @@ import { } from "../constants/responses"; import { DISCORD_BASE_URL } from "../constants/urls"; -async function muteUser( - userId: string, - guildId: string, - token: string -): Promise { - try { - const response = await fetch( - `${DISCORD_BASE_URL}/guilds/${guildId}/members/${userId}`, - { - method: "PATCH", - headers: { - "Content-Type": "application/json", - Authorization: `Bot ${token}`, - }, - body: JSON.stringify({ - mute: true, - channel_id: null, - }), - } - ); - - if (response.ok) { - console.log("User muted successfully"); - } else { - console.error(`Error: ${response.status} - ${response.statusText}`); - } - } catch (error) { - console.error("Error occurred:", error); - } -} - -async function unmuteUser( - userId: string, - guildId: string, - token: string -): Promise { - try { - const response = await fetch( - `${DISCORD_BASE_URL}/guilds/${guildId}/members/${userId}`, - { - method: "PATCH", - headers: { - "Content-Type": "application/json", - Authorization: `Bot ${token}`, - }, - body: JSON.stringify({ - mute: false, - channel_id: null, - }), - } - ); - - if (response.ok) { - console.log("User unmuted successfully"); - } else { - console.error(`Error: ${response.status} - ${response.statusText}`); - } - } catch (error) { - console.error("Error occurred:", error); - } -} - export async function baseHandler( message: discordMessageRequest, env: env @@ -124,15 +60,12 @@ export async function baseHandler( } case getCommandName(MENTION_EACH): { const data = message.data?.options as Array; - // data[0] is role obj - // data[1] is message obj const transformedArgument = { roleToBeTaggedObj: data[0], displayMessageObj: data[1] ?? {}, }; return await mentionEachUser(transformedArgument, env); } - case getCommandName(LISTENING): { const data = message.data?.options; const setter = data ? data[0].value : false; @@ -182,7 +115,6 @@ export async function baseHandler( const data = message.data?.options as Array; return await oooCommand(data[0].value); } - case getCommandName(USER): { const data = message.data?.options as Array; return await userCommand(data[0].value, env); diff --git a/src/utils/userMuteUnmuteActions.ts b/src/utils/userMuteUnmuteActions.ts new file mode 100644 index 00000000..2d3c42a9 --- /dev/null +++ b/src/utils/userMuteUnmuteActions.ts @@ -0,0 +1,63 @@ +import { DISCORD_BASE_URL } from "../constants/urls"; + +export async function muteUser( + userId: string, + guildId: string, + token: string +): Promise { + try { + const response = await fetch( + `${DISCORD_BASE_URL}/guilds/${guildId}/members/${userId}`, + { + method: "PATCH", + headers: { + "Content-Type": "application/json", + Authorization: `Bot ${token}`, + }, + body: JSON.stringify({ + mute: true, + channel_id: null, + }), + } + ); + + if (response.ok) { + console.log("User muted successfully"); + } else { + console.error(`Error: ${response.status} - ${response.statusText}`); + } + } catch (error) { + console.error("Error occurred:", error); + } +} + +export async function unmuteUser( + userId: string, + guildId: string, + token: string +): Promise { + try { + const response = await fetch( + `${DISCORD_BASE_URL}/guilds/${guildId}/members/${userId}`, + { + method: "PATCH", + headers: { + "Content-Type": "application/json", + Authorization: `Bot ${token}`, + }, + body: JSON.stringify({ + mute: false, + channel_id: null, + }), + } + ); + + if (response.ok) { + console.log("User unmuted successfully"); + } else { + console.error(`Error: ${response.status} - ${response.statusText}`); + } + } catch (error) { + console.error("Error occurred:", error); + } +} From 506c5723ad82db1960b90c39c3ff20d7df585612 Mon Sep 17 00:00:00 2001 From: Achintya-Chatterjee Date: Wed, 13 Mar 2024 04:15:20 +0530 Subject: [PATCH 04/11] refactor: add space --- src/controllers/baseHandler.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/controllers/baseHandler.ts b/src/controllers/baseHandler.ts index 2f43e892..f1b00214 100644 --- a/src/controllers/baseHandler.ts +++ b/src/controllers/baseHandler.ts @@ -10,11 +10,13 @@ import { muteUser, unmuteUser } from "../utils/userMuteUnmuteActions"; import { getCommandName } from "../utils/getCommandName"; import JSONResponse from "../utils/JsonResponse"; import { lowerCaseMessageCommands } from "../utils/lowerCaseMessageCommand"; + import { env } from "../typeDefinitions/default.types"; import { discordMessageRequest, messageRequestDataOptions, } from "../typeDefinitions/discordMessage.types"; + import { HELLO, LISTENING, @@ -60,10 +62,13 @@ export async function baseHandler( } case getCommandName(MENTION_EACH): { const data = message.data?.options as Array; + // data[0] is role obj + // data[1] is message obj const transformedArgument = { roleToBeTaggedObj: data[0], displayMessageObj: data[1] ?? {}, }; + return await mentionEachUser(transformedArgument, env); } case getCommandName(LISTENING): { From a0a62e061b7d0020dbf35e845ba4706382b32c7c Mon Sep 17 00:00:00 2001 From: Achintya-Chatterjee Date: Wed, 13 Mar 2024 04:17:05 +0530 Subject: [PATCH 05/11] refactor: add space --- src/controllers/baseHandler.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/controllers/baseHandler.ts b/src/controllers/baseHandler.ts index f1b00214..efb85d7c 100644 --- a/src/controllers/baseHandler.ts +++ b/src/controllers/baseHandler.ts @@ -71,6 +71,7 @@ export async function baseHandler( return await mentionEachUser(transformedArgument, env); } + case getCommandName(LISTENING): { const data = message.data?.options; const setter = data ? data[0].value : false; From 14cdc8b54930e503e8a201e2d2ef82e01a96d832 Mon Sep 17 00:00:00 2001 From: Achintya-Chatterjee Date: Tue, 30 Apr 2024 01:23:43 +0530 Subject: [PATCH 06/11] add: listening command --- src/controllers/baseHandler.ts | 51 ++++++++++++-- src/utils/discordAPI.ts | 120 +++++++++++++++++++++++++++++++++ 2 files changed, 167 insertions(+), 4 deletions(-) create mode 100644 src/utils/discordAPI.ts diff --git a/src/controllers/baseHandler.ts b/src/controllers/baseHandler.ts index efb85d7c..527f70b7 100644 --- a/src/controllers/baseHandler.ts +++ b/src/controllers/baseHandler.ts @@ -41,6 +41,14 @@ import { } from "../constants/responses"; import { DISCORD_BASE_URL } from "../constants/urls"; +// Import necessary functions from DiscordAPI.ts +import { + createMutedRole, + assignRoleToUser, + removeRoleFromUser, + getMutedRoleId, +} from "../utils/discordAPI"; + export async function baseHandler( message: discordMessageRequest, env: env @@ -62,8 +70,6 @@ export async function baseHandler( } case getCommandName(MENTION_EACH): { const data = message.data?.options as Array; - // data[0] is role obj - // data[1] is message obj const transformedArgument = { roleToBeTaggedObj: data[0], displayMessageObj: data[1] ?? {}, @@ -85,7 +91,25 @@ export async function baseHandler( updateNickNameData = NICKNAME_PREFIX + message.member.user.username + NICKNAME_SUFFIX; discordEphemeral = LISTENING_SUCCESS_MESSAGE; - await muteUser(memberId, message.guild.id, env.DISCORD_TOKEN); + // Create the muted role if it doesn't exist + const mutedRoleId = await createMutedRole( + message.guild.id, + env.DISCORD_TOKEN + ); + if (mutedRoleId) { + // Assign the muted role to the user + await assignRoleToUser( + message.guild.id, + memberId, + mutedRoleId, + env.DISCORD_TOKEN + ); + // Mute the user + await muteUser(memberId, message.guild.id, env.DISCORD_TOKEN); + } else { + console.error("Failed to create muted role."); + return commandNotFound(); // Return an error response + } } else { updateNickNameData = message.member.nick; discordEphemeral = ALREADY_LISTENING; @@ -93,7 +117,24 @@ export async function baseHandler( } else { updateNickNameData = nickname; discordEphemeral = REMOVED_LISTENING_MESSAGE; - await unmuteUser(memberId, message.guild.id, env.DISCORD_TOKEN); + // Remove the muted role from the user + const mutedRoleId = await getMutedRoleId( + message.guild.id, + env.DISCORD_TOKEN + ); + if (mutedRoleId) { + await removeRoleFromUser( + message.guild.id, + memberId, + mutedRoleId, + env.DISCORD_TOKEN + ); + // Unmute the user + await unmuteUser(memberId, message.guild.id, env.DISCORD_TOKEN); + } else { + console.error("Muted role not found."); + return commandNotFound(); // Return an error response + } } await updateNickName( message.member.user.id.toString(), @@ -102,9 +143,11 @@ export async function baseHandler( ); return discordEphemeralResponse(discordEphemeral); } catch (err) { + console.error("Error:", err); return discordEphemeralResponse(RETRY_COMMAND); } } + case getCommandName(TASK): { const data = message.data?.options as Array; return await taskCommand(data[0].value); diff --git a/src/utils/discordAPI.ts b/src/utils/discordAPI.ts new file mode 100644 index 00000000..aaa83d82 --- /dev/null +++ b/src/utils/discordAPI.ts @@ -0,0 +1,120 @@ +// src/utils/DiscordAPI.ts + +import fetch from "node-fetch"; +import { DISCORD_BASE_URL } from "../constants/urls"; + +export async function createMutedRole( + guildId: string, + token: string +): Promise { + try { + const response = await fetch( + `${DISCORD_BASE_URL}/guilds/${guildId}/roles`, + { + method: "POST", + headers: { + "Content-Type": "application/json", + Authorization: `Bot ${token}`, + }, + body: JSON.stringify({ + name: "Muted", + permissions: "0", + color: 0, + hoist: false, + mentionable: false, + }), + } + ); + + if (response.ok) { + const data = await response.json(); + return data.id; + } else { + console.error( + `Error creating muted role: ${response.status} - ${response.statusText}` + ); + return null; + } + } catch (error) { + console.error("Error occurred while creating muted role:", error); + return null; + } +} + +export async function assignRoleToUser( + guildId: string, + userId: string, + roleId: string, + token: string +): Promise { + try { + await fetch( + `${DISCORD_BASE_URL}/guilds/${guildId}/members/${userId}/roles/${roleId}`, + { + method: "PUT", + headers: { + "Content-Type": "application/json", + Authorization: `Bot ${token}`, + }, + } + ); + } catch (error) { + console.error("Error occurred while assigning role to user:", error); + } +} + +export async function removeRoleFromUser( + guildId: string, + userId: string, + roleId: string, + token: string +): Promise { + try { + await fetch( + `${DISCORD_BASE_URL}/guilds/${guildId}/members/${userId}/roles/${roleId}`, + { + method: "DELETE", + headers: { + "Content-Type": "application/json", + Authorization: `Bot ${token}`, + }, + } + ); + } catch (error) { + console.error("Error occurred while removing role from user:", error); + } +} + +export async function getMutedRoleId( + guildId: string, + token: string +): Promise { + try { + const response = await fetch( + `${DISCORD_BASE_URL}/guilds/${guildId}/roles`, + { + method: "GET", + headers: { + "Content-Type": "application/json", + Authorization: `Bot ${token}`, + }, + } + ); + + if (response.ok) { + const roles = await response.json(); + const mutedRole = roles.find( + (role: any) => role.name.toLowerCase() === "muted" + ); + return mutedRole ? mutedRole.id : null; + } else { + console.error( + `Error fetching roles: ${response.status} - ${response.statusText}` + ); + return null; + } + } catch (error) { + console.error("Error occurred while fetching roles:", error); + return null; + } +} From c9bb140cbf058bab1bac9d0861c06b1a7309c52d Mon Sep 17 00:00:00 2001 From: Achintya-Chatterjee Date: Tue, 30 Apr 2024 03:28:38 +0530 Subject: [PATCH 07/11] add: listening command --- src/controllers/baseHandler.ts | 71 ++++++++++++++-------------------- src/utils/discordAPI.ts | 23 +++++++---- 2 files changed, 46 insertions(+), 48 deletions(-) diff --git a/src/controllers/baseHandler.ts b/src/controllers/baseHandler.ts index 527f70b7..26aa4c16 100644 --- a/src/controllers/baseHandler.ts +++ b/src/controllers/baseHandler.ts @@ -87,33 +87,27 @@ export async function baseHandler( let updateNickNameData = ""; try { if (setter) { - if (!message.member.nick?.includes(NICKNAME_SUFFIX)) { - updateNickNameData = - NICKNAME_PREFIX + message.member.user.username + NICKNAME_SUFFIX; - discordEphemeral = LISTENING_SUCCESS_MESSAGE; - // Create the muted role if it doesn't exist - const mutedRoleId = await createMutedRole( - message.guild.id, - env.DISCORD_TOKEN - ); - if (mutedRoleId) { - // Assign the muted role to the user - await assignRoleToUser( - message.guild.id, - memberId, - mutedRoleId, - env.DISCORD_TOKEN - ); - // Mute the user - await muteUser(memberId, message.guild.id, env.DISCORD_TOKEN); - } else { - console.error("Failed to create muted role."); - return commandNotFound(); // Return an error response - } - } else { - updateNickNameData = message.member.nick; - discordEphemeral = ALREADY_LISTENING; + updateNickNameData = + NICKNAME_PREFIX + message.member.user.username + NICKNAME_SUFFIX; + discordEphemeral = LISTENING_SUCCESS_MESSAGE; + // Create the muted role if it doesn't exist + const mutedRoleId = await getMutedRoleId( + message.guild.id, + env.DISCORD_TOKEN + ); + if (!mutedRoleId) { + console.error("Muted role not found."); + return commandNotFound(); // Return an error response } + // Assign the muted role to the user + await assignRoleToUser( + message.guild.id, + memberId, + mutedRoleId, + env.DISCORD_TOKEN + ); + // Mute the user + await muteUser(memberId, message.guild.id, env.DISCORD_TOKEN); } else { updateNickNameData = nickname; discordEphemeral = REMOVED_LISTENING_MESSAGE; @@ -122,25 +116,20 @@ export async function baseHandler( message.guild.id, env.DISCORD_TOKEN ); - if (mutedRoleId) { - await removeRoleFromUser( - message.guild.id, - memberId, - mutedRoleId, - env.DISCORD_TOKEN - ); - // Unmute the user - await unmuteUser(memberId, message.guild.id, env.DISCORD_TOKEN); - } else { + if (!mutedRoleId) { console.error("Muted role not found."); return commandNotFound(); // Return an error response } + await removeRoleFromUser( + message.guild.id, + memberId, + mutedRoleId, + env.DISCORD_TOKEN + ); + // Unmute the user + await unmuteUser(memberId, message.guild.id, env.DISCORD_TOKEN); } - await updateNickName( - message.member.user.id.toString(), - updateNickNameData, - env - ); + await updateNickName(memberId, updateNickNameData, env); return discordEphemeralResponse(discordEphemeral); } catch (err) { console.error("Error:", err); diff --git a/src/utils/discordAPI.ts b/src/utils/discordAPI.ts index aaa83d82..4df08530 100644 --- a/src/utils/discordAPI.ts +++ b/src/utils/discordAPI.ts @@ -1,8 +1,10 @@ -// src/utils/DiscordAPI.ts - -import fetch from "node-fetch"; import { DISCORD_BASE_URL } from "../constants/urls"; +interface CreateRoleResponse { + id: string; + // Add other properties as needed +} + export async function createMutedRole( guildId: string, token: string @@ -27,7 +29,7 @@ export async function createMutedRole( ); if (response.ok) { - const data = await response.json(); + const data: CreateRoleResponse = await response.json(); // Specify the type as CreateRoleResponse return data.id; } else { console.error( @@ -40,7 +42,6 @@ export async function createMutedRole( return null; } } - export async function assignRoleToUser( guildId: string, userId: string, @@ -85,6 +86,14 @@ export async function removeRoleFromUser( } } +// Define the type of 'data' and 'roles' +interface Role { + id: string; + name: string; + // Add other properties as needed +} + +// Use the defined interface for 'roles' and 'data' export async function getMutedRoleId( guildId: string, token: string @@ -102,9 +111,9 @@ export async function getMutedRoleId( ); if (response.ok) { - const roles = await response.json(); + const roles: Role[] = await response.json(); // Specify the type as Role[] const mutedRole = roles.find( - (role: any) => role.name.toLowerCase() === "muted" + (role) => role.name.toLowerCase() === "muted" ); return mutedRole ? mutedRole.id : null; } else { From b5c94c51b93496f8cf7c8ee18b6f1af900b49859 Mon Sep 17 00:00:00 2001 From: Achintya-Chatterjee Date: Tue, 30 Apr 2024 12:12:22 +0530 Subject: [PATCH 08/11] fix:lint --- src/controllers/baseHandler.ts | 11 ++--------- src/utils/discordAPI.ts | 8 ++------ 2 files changed, 4 insertions(+), 15 deletions(-) diff --git a/src/controllers/baseHandler.ts b/src/controllers/baseHandler.ts index 26aa4c16..0b9c7f21 100644 --- a/src/controllers/baseHandler.ts +++ b/src/controllers/baseHandler.ts @@ -40,8 +40,6 @@ import { RETRY_COMMAND, } from "../constants/responses"; import { DISCORD_BASE_URL } from "../constants/urls"; - -// Import necessary functions from DiscordAPI.ts import { createMutedRole, assignRoleToUser, @@ -90,35 +88,31 @@ export async function baseHandler( updateNickNameData = NICKNAME_PREFIX + message.member.user.username + NICKNAME_SUFFIX; discordEphemeral = LISTENING_SUCCESS_MESSAGE; - // Create the muted role if it doesn't exist const mutedRoleId = await getMutedRoleId( message.guild.id, env.DISCORD_TOKEN ); if (!mutedRoleId) { console.error("Muted role not found."); - return commandNotFound(); // Return an error response + return commandNotFound(); } - // Assign the muted role to the user await assignRoleToUser( message.guild.id, memberId, mutedRoleId, env.DISCORD_TOKEN ); - // Mute the user await muteUser(memberId, message.guild.id, env.DISCORD_TOKEN); } else { updateNickNameData = nickname; discordEphemeral = REMOVED_LISTENING_MESSAGE; - // Remove the muted role from the user const mutedRoleId = await getMutedRoleId( message.guild.id, env.DISCORD_TOKEN ); if (!mutedRoleId) { console.error("Muted role not found."); - return commandNotFound(); // Return an error response + return commandNotFound(); } await removeRoleFromUser( message.guild.id, @@ -126,7 +120,6 @@ export async function baseHandler( mutedRoleId, env.DISCORD_TOKEN ); - // Unmute the user await unmuteUser(memberId, message.guild.id, env.DISCORD_TOKEN); } await updateNickName(memberId, updateNickNameData, env); diff --git a/src/utils/discordAPI.ts b/src/utils/discordAPI.ts index 4df08530..ca07e11a 100644 --- a/src/utils/discordAPI.ts +++ b/src/utils/discordAPI.ts @@ -2,7 +2,6 @@ import { DISCORD_BASE_URL } from "../constants/urls"; interface CreateRoleResponse { id: string; - // Add other properties as needed } export async function createMutedRole( @@ -29,7 +28,7 @@ export async function createMutedRole( ); if (response.ok) { - const data: CreateRoleResponse = await response.json(); // Specify the type as CreateRoleResponse + const data: CreateRoleResponse = await response.json(); return data.id; } else { console.error( @@ -86,14 +85,11 @@ export async function removeRoleFromUser( } } -// Define the type of 'data' and 'roles' interface Role { id: string; name: string; - // Add other properties as needed } -// Use the defined interface for 'roles' and 'data' export async function getMutedRoleId( guildId: string, token: string @@ -111,7 +107,7 @@ export async function getMutedRoleId( ); if (response.ok) { - const roles: Role[] = await response.json(); // Specify the type as Role[] + const roles: Role[] = await response.json(); const mutedRole = roles.find( (role) => role.name.toLowerCase() === "muted" ); From 0d365126300803487a2395cf1aab6f96f86836d3 Mon Sep 17 00:00:00 2001 From: Achintya-Chatterjee Date: Tue, 30 Apr 2024 12:16:05 +0530 Subject: [PATCH 09/11] fix:lint --- tests/fixtures/fixture.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/fixtures/fixture.ts b/tests/fixtures/fixture.ts index b56664d8..6f9ad172 100644 --- a/tests/fixtures/fixture.ts +++ b/tests/fixtures/fixture.ts @@ -28,7 +28,6 @@ export const dummyHelloMessage: discordMessageRequest = { guild: undefined, channel_id: 123456, - }; export const dummyVerifyMessage: discordMessageRequest = { @@ -50,7 +49,6 @@ export const dummyVerifyMessage: discordMessageRequest = { guild: undefined, channel_id: 123456, - }; export const dummyCreateBody: createNewRole = { From 8479b5827a44c87eb1a85ebab62da0ef32ff6850 Mon Sep 17 00:00:00 2001 From: Achintya-Chatterjee Date: Tue, 30 Apr 2024 17:39:46 +0530 Subject: [PATCH 10/11] add: test cases for mute and unmute users --- package-lock.json | 126 ++++++++++++++++++ package.json | 1 + src/utils/userMuteUnmuteActions.ts | 2 + .../unit/utils/userMuteUnmuteActions.test.ts | 96 +++++++++++++ 4 files changed, 225 insertions(+) create mode 100644 tests/unit/utils/userMuteUnmuteActions.test.ts diff --git a/package-lock.json b/package-lock.json index 7f88fd0c..a6270d30 100644 --- a/package-lock.json +++ b/package-lock.json @@ -24,6 +24,7 @@ "@typescript-eslint/parser": "^5.47.1", "eslint": "^8.31.0", "jest": "^29.3.1", + "jest-fetch-mock": "^3.0.3", "ngrok": "^5.0.0-beta.2", "pre-commit": "^1.2.2", "prettier": "^2.8.1", @@ -2880,6 +2881,35 @@ "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", "dev": true }, + "node_modules/cross-fetch": { + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.8.tgz", + "integrity": "sha512-cvA+JwZoU0Xq+h6WkMvAUqPEYy92Obet6UdKLfW60qn99ftItKjB5T+BkyWOFWe2pUyfQ+IJHmpOTznqk1M6Kg==", + "dev": true, + "dependencies": { + "node-fetch": "^2.6.12" + } + }, + "node_modules/cross-fetch/node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "dev": true, + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, "node_modules/cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -4507,6 +4537,16 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, + "node_modules/jest-fetch-mock": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/jest-fetch-mock/-/jest-fetch-mock-3.0.3.tgz", + "integrity": "sha512-Ux1nWprtLrdrH4XwE7O7InRY6psIi3GOsqNESJgMJ+M5cv4A8Lh7SN9d2V2kKRZ8ebAfcd1LNyZguAOb6JiDqw==", + "dev": true, + "dependencies": { + "cross-fetch": "^3.0.4", + "promise-polyfill": "^8.1.3" + } + }, "node_modules/jest-get-type": { "version": "29.2.0", "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.2.0.tgz", @@ -5807,6 +5847,12 @@ "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", "dev": true }, + "node_modules/promise-polyfill": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/promise-polyfill/-/promise-polyfill-8.3.0.tgz", + "integrity": "sha512-H5oELycFml5yto/atYqmjyigJoAo3+OXwolYiH7OfQuYlAqhxNvTfiNMbV9hsC6Yp83yE5r2KTVmtrG6R9i6Pg==", + "dev": true + }, "node_modules/prompts": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", @@ -6520,6 +6566,12 @@ "node": ">=8.0" } }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "dev": true + }, "node_modules/ts-jest": { "version": "29.0.5", "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.0.5.tgz", @@ -6802,6 +6854,22 @@ "node": ">= 8" } }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "dev": true + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dev": true, + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -9086,6 +9154,26 @@ "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", "dev": true }, + "cross-fetch": { + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.8.tgz", + "integrity": "sha512-cvA+JwZoU0Xq+h6WkMvAUqPEYy92Obet6UdKLfW60qn99ftItKjB5T+BkyWOFWe2pUyfQ+IJHmpOTznqk1M6Kg==", + "dev": true, + "requires": { + "node-fetch": "^2.6.12" + }, + "dependencies": { + "node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "dev": true, + "requires": { + "whatwg-url": "^5.0.0" + } + } + } + }, "cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -10284,6 +10372,16 @@ "jest-util": "^29.3.1" } }, + "jest-fetch-mock": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/jest-fetch-mock/-/jest-fetch-mock-3.0.3.tgz", + "integrity": "sha512-Ux1nWprtLrdrH4XwE7O7InRY6psIi3GOsqNESJgMJ+M5cv4A8Lh7SN9d2V2kKRZ8ebAfcd1LNyZguAOb6JiDqw==", + "dev": true, + "requires": { + "cross-fetch": "^3.0.4", + "promise-polyfill": "^8.1.3" + } + }, "jest-get-type": { "version": "29.2.0", "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.2.0.tgz", @@ -11281,6 +11379,12 @@ "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", "dev": true }, + "promise-polyfill": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/promise-polyfill/-/promise-polyfill-8.3.0.tgz", + "integrity": "sha512-H5oELycFml5yto/atYqmjyigJoAo3+OXwolYiH7OfQuYlAqhxNvTfiNMbV9hsC6Yp83yE5r2KTVmtrG6R9i6Pg==", + "dev": true + }, "prompts": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", @@ -11804,6 +11908,12 @@ "is-number": "^7.0.0" } }, + "tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "dev": true + }, "ts-jest": { "version": "29.0.5", "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.0.5.tgz", @@ -11986,6 +12096,22 @@ "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.2.1.tgz", "integrity": "sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q==" }, + "webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "dev": true + }, + "whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dev": true, + "requires": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, "which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", diff --git a/package.json b/package.json index ed1a11d8..b769be4c 100644 --- a/package.json +++ b/package.json @@ -29,6 +29,7 @@ "@typescript-eslint/parser": "^5.47.1", "eslint": "^8.31.0", "jest": "^29.3.1", + "jest-fetch-mock": "^3.0.3", "ngrok": "^5.0.0-beta.2", "pre-commit": "^1.2.2", "prettier": "^2.8.1", diff --git a/src/utils/userMuteUnmuteActions.ts b/src/utils/userMuteUnmuteActions.ts index 2d3c42a9..24cb26a4 100644 --- a/src/utils/userMuteUnmuteActions.ts +++ b/src/utils/userMuteUnmuteActions.ts @@ -28,6 +28,7 @@ export async function muteUser( } } catch (error) { console.error("Error occurred:", error); + throw error; } } @@ -59,5 +60,6 @@ export async function unmuteUser( } } catch (error) { console.error("Error occurred:", error); + throw error; } } diff --git a/tests/unit/utils/userMuteUnmuteActions.test.ts b/tests/unit/utils/userMuteUnmuteActions.test.ts new file mode 100644 index 00000000..0dc9f3d4 --- /dev/null +++ b/tests/unit/utils/userMuteUnmuteActions.test.ts @@ -0,0 +1,96 @@ +import { DISCORD_BASE_URL } from "../../../src/constants/urls"; +import { muteUser, unmuteUser } from "../../../src/utils/userMuteUnmuteActions"; +import fetchMock from "jest-fetch-mock"; // Import jest-fetch-mock + +jest.mock("node-fetch", () => fetchMock); // Mock node-fetch module + +// Mock environment variables +const mockEnv = { + DISCORD_BASE_URL: DISCORD_BASE_URL, // Use the same Discord base URL as in the src/constants/urls file + DISCORD_TOKEN: "mockToken", +}; + +describe("User Mute and Unmute Actions", () => { + let userId: string; + let guildId: string; + + beforeEach(() => { + userId = "123456789"; + guildId = "987654321"; + }); + + afterEach(() => { + jest.resetAllMocks(); // Reset mock state after each test + }); + + const assertFetchCall = (url: string, bodyObj: any) => { + expect(fetch).toHaveBeenCalledWith(url, { + method: "PATCH", + headers: { + "Content-Type": "application/json", + Authorization: `Bot ${mockEnv.DISCORD_TOKEN}`, + }, + body: JSON.stringify(bodyObj), + }); + }; + + it("should mute a user successfully", async () => { + const url = `${mockEnv.DISCORD_BASE_URL}/guilds/${guildId}/members/${userId}`; + const bodyObj = { + mute: true, + channel_id: null, + }; + + jest + .spyOn(global, "fetch") + .mockImplementation(() => Promise.resolve(new Response())); + + await muteUser(userId, guildId, mockEnv.DISCORD_TOKEN); + + assertFetchCall(url, bodyObj); + }); + + it("should handle errors when muting a user", async () => { + const url = `${mockEnv.DISCORD_BASE_URL}/guilds/${guildId}/members/${userId}`; + + jest + .spyOn(global, "fetch") + .mockRejectedValueOnce(new Error("Failed to mute user")); + + await expect( + muteUser(userId, guildId, mockEnv.DISCORD_TOKEN) + ).rejects.toThrowError("Failed to mute user"); + + expect(fetch).toHaveBeenCalledWith(url, expect.any(Object)); + }); + + it("should unmute a user successfully", async () => { + const url = `${mockEnv.DISCORD_BASE_URL}/guilds/${guildId}/members/${userId}`; + const bodyObj = { + mute: false, + channel_id: null, + }; + + jest + .spyOn(global, "fetch") + .mockImplementation(() => Promise.resolve(new Response())); + + await unmuteUser(userId, guildId, mockEnv.DISCORD_TOKEN); + + assertFetchCall(url, bodyObj); + }); + + it("should handle errors when unmuting a user", async () => { + const url = `${mockEnv.DISCORD_BASE_URL}/guilds/${guildId}/members/${userId}`; + + jest + .spyOn(global, "fetch") + .mockRejectedValueOnce(new Error("Failed to unmute user")); + + await expect( + unmuteUser(userId, guildId, mockEnv.DISCORD_TOKEN) + ).rejects.toThrowError("Failed to unmute user"); + + expect(fetch).toHaveBeenCalledWith(url, expect.any(Object)); + }); +}); From c3fa79fb571a251f9509ff915155db26a185226c Mon Sep 17 00:00:00 2001 From: Achintya-Chatterjee Date: Tue, 30 Apr 2024 17:44:22 +0530 Subject: [PATCH 11/11] chore: remove unnecessery files --- tests/unit/utils/userMuteUnmuteActions.test.ts | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/tests/unit/utils/userMuteUnmuteActions.test.ts b/tests/unit/utils/userMuteUnmuteActions.test.ts index 0dc9f3d4..64d07f08 100644 --- a/tests/unit/utils/userMuteUnmuteActions.test.ts +++ b/tests/unit/utils/userMuteUnmuteActions.test.ts @@ -1,12 +1,9 @@ import { DISCORD_BASE_URL } from "../../../src/constants/urls"; import { muteUser, unmuteUser } from "../../../src/utils/userMuteUnmuteActions"; -import fetchMock from "jest-fetch-mock"; // Import jest-fetch-mock - -jest.mock("node-fetch", () => fetchMock); // Mock node-fetch module - -// Mock environment variables +import fetchMock from "jest-fetch-mock"; +jest.mock("node-fetch", () => fetchMock); const mockEnv = { - DISCORD_BASE_URL: DISCORD_BASE_URL, // Use the same Discord base URL as in the src/constants/urls file + DISCORD_BASE_URL: DISCORD_BASE_URL, DISCORD_TOKEN: "mockToken", }; @@ -20,7 +17,7 @@ describe("User Mute and Unmute Actions", () => { }); afterEach(() => { - jest.resetAllMocks(); // Reset mock state after each test + jest.resetAllMocks(); }); const assertFetchCall = (url: string, bodyObj: any) => {