refactor: Removing then/catch from async/await calls (#22)

* refactored async/await for import helper to not also use then/catch

* added enum

* refactor webhookHelper and tests to not use then/catch

* changed docstring

* refactoring bot and tests to not use then/catch

* refactoring commands.js and tests to not use then/catch

* refactoring memberHelper.js and tests to not use then/catch

* removing then/catch from messageHelper.test.js

* fixed set up for commands tests

* edited bot to have top level main function

* one more test in commands.js, and removed console.error

* fixed typo in webhookHelper

* forgot to switch over some tests in bot.test and commands.test

* removed console.log from import helper

* put console.error in commands

* converted utils.js to not use then/catch

* tested utils checkImageFormatValidity

* removed jest-fetch-mock since it turns out I was just manually mocking it anyway

* refactored database to not use then/catch

* added dash to commands.js and test to pass

* added the remaining webhook tests

* changed utils to check for 10MB size not 1MB

* removed unnecessary try/catch from utils

* Simplify getWebhook to use .find() instead of foreach logic

* make memberCommand exit when error occurs with parseMemberCommand

* changed commands.js to not have user interaction within the catch

* updated console.error message in database.js

* made importHelper mock throw error instead of "resolve" error

* replaced "pk;" with "pf;" in test

* Got rid of unnecessary check for empty message from user (Fluxer doesn't allow this to happen)

Removed export of token

* getAllMembersInfo checks for fields.length

* added default case to memberCommandHandler to throw error if command is not recognized

* reversed check for valid proxy (was returning valid if the proxy existed and invalid if it didn't)

* pushes e.message instead of full error object to errors array in importHelper

* adjusted tests to properly use mockRejectedValue for async rejections

* changed getAllMembersInfo map to say `index` not `name` as it actually gets the index of a member and then the member object

* adjusted importHelper to properly test throwing of aggregate error

* revamped setting of expiration warning (moved to utils and changed logic, wrote tests)

---------

Co-authored-by: Aster Fialla <asterfialla@gmail.com>
This commit is contained in:
2026-02-25 19:30:39 -05:00
committed by GitHub
parent 7fead5e3d7
commit df80eca0ec
18 changed files with 987 additions and 965 deletions

View File

@@ -22,7 +22,6 @@ const newAndRemoveCommands = ['new', 'remove'];
* @returns {Promise <EmbedBuilder>} A list of 25 members as an embed.
* @returns {Promise <EmbedBuilder>} A list of member commands and descriptions.
* @returns {Promise<{EmbedBuilder, string[], string}>} A member info embed + info/errors.
* @throws {Error}
*/
mh.parseMemberCommand = async function (authorId, authorFull, args, attachmentUrl = null, attachmentExpiration = null) {
let memberName, command, isHelp = false;
@@ -72,7 +71,7 @@ mh.parseMemberCommand = async function (authorId, authorFull, args, attachmentUr
* @returns {Promise <EmbedBuilder>} A list of 25 members as an embed.
* @returns {Promise <EmbedBuilder>} A list of member commands and descriptions.
* @returns {Promise<{EmbedBuilder, [string], string}>} A member info embed + info/errors.
* @throws {Error}
* @throws {Error} When there's no member or a command is not recognized.
*/
mh.memberArgumentHandler = async function(authorId, authorFull, isHelp, command = null, memberName = null, args = [], attachmentUrl = null, attachmentExpiration = null) {
if (!command && !memberName && !isHelp) {
@@ -93,10 +92,10 @@ mh.memberArgumentHandler = async function(authorId, authorFull, isHelp, command
// ex: pf;member blah blah
if (command && memberName && (values.length > 0 || newAndRemoveCommands.includes(command) || attachmentUrl)) {
return await mh.memberCommandHandler(authorId, command, memberName, values, attachmentUrl, attachmentExpiration).catch((e) => {throw e});
return await mh.memberCommandHandler(authorId, command, memberName, values, attachmentUrl, attachmentExpiration);
}
else if (memberName && values.length === 0) {
return await mh.sendCurrentValue(authorId, memberName, command).catch((e) => {throw e});
return await mh.sendCurrentValue(authorId, memberName, command);
}
}
@@ -111,12 +110,11 @@ mh.memberArgumentHandler = async function(authorId, authorFull, isHelp, command
* @returns {Promise <EmbedBuilder>} A list of 25 members as an embed.
* @returns {Promise <EmbedBuilder>} A list of member commands and descriptions.
* @returns {Promise<{EmbedBuilder, string[], string}>} A member info embed + info/errors.
* @throws {Error} When there's no member
*/
mh.sendCurrentValue = async function(authorId, memberName, command= null) {
const member = await mh.getMemberByName(authorId, memberName).then((m) => {
if (!m) throw new Error(enums.err.NO_MEMBER);
return m;
});
const member = await mh.getMemberByName(authorId, memberName);
if (!member) throw new Error(enums.err.NO_MEMBER);
if (!command) {
return mh.getMemberInfo(member);
@@ -173,22 +171,23 @@ mh.sendHelpEnum = function(command) {
* @returns {Promise <EmbedBuilder>} A list of 25 members as an embed.
* @returns {Promise <EmbedBuilder>} A list of member commands and descriptions.
* @returns {Promise<{EmbedBuilder, [string], string}>} A member info embed + info/errors.
* @throws {Error}
*/
mh.memberCommandHandler = async function(authorId, command, memberName, values, attachmentUrl = null, attachmentExpiration = null) {
switch (command) {
case 'new':
return await mh.addNewMember(authorId, memberName, values, attachmentUrl, attachmentExpiration).catch((e) => {throw e});
return await mh.addNewMember(authorId, memberName, values, attachmentUrl, attachmentExpiration);
case 'remove':
return await mh.removeMember(authorId, memberName).catch((e) => {throw e});
return await mh.removeMember(authorId, memberName);
case 'name':
return await mh.updateName(authorId, memberName, values[0]).catch((e) => {throw e});
return await mh.updateName(authorId, memberName, values[0]);
case 'displayname':
return await mh.updateDisplayName(authorId, memberName, values[0]).catch((e) => {throw e});
return await mh.updateDisplayName(authorId, memberName, values[0]);
case 'proxy':
return await mh.updateProxy(authorId, memberName, values[0]).catch((e) => {throw e});
return await mh.updateProxy(authorId, memberName, values[0]);
case 'propic':
return await mh.updatePropic(authorId, memberName, values[0], attachmentUrl, attachmentExpiration).catch((e) => {throw e});
return await mh.updatePropic(authorId, memberName, values[0], attachmentUrl, attachmentExpiration);
default:
throw new Error(enums.err.COMMAND_NOT_RECOGNIZED);
}
}
@@ -202,20 +201,15 @@ mh.memberCommandHandler = async function(authorId, command, memberName, values,
* @param {string | null} [attachmentUrl] - The attachment URL, if any
* @param {string | null} [attachmentExpiration] - The attachment expiry date, if any
* @returns {Promise<{EmbedBuilder, string[], string}>} A successful addition.
* @throws {Error} When creating a member doesn't work.
*/
mh.addNewMember = async function (authorId, memberName, values, attachmentUrl = null, attachmentExpiration = null) {
const displayName = values[0];
const proxy = values[1];
const propic = values[2] ?? attachmentUrl;
return await mh.addFullMember(authorId, memberName, displayName, proxy, propic, attachmentExpiration).then((response) => {
const memberInfoEmbed = mh.getMemberInfo(response.member);
return {embed: memberInfoEmbed, errors: response.errors, success: `${memberName} has been added successfully.`};
}).catch(e => {
console.error(e);
throw e;
})
const memberObj = await mh.addFullMember(authorId, memberName, displayName, proxy, propic, attachmentExpiration);
const memberInfoEmbed = mh.getMemberInfo(memberObj.member);
return {embed: memberInfoEmbed, errors: memberObj.errors, success: `${memberName} has been added successfully.`}
}
/**
@@ -233,9 +227,7 @@ mh.updateName = async function (authorId, memberName, name) {
if (trimmedName === '') {
throw new RangeError(`Name ${enums.err.NO_VALUE}`);
}
return await mh.updateMemberField(authorId, memberName, "name", trimmedName).catch((e) => {
throw e
});
return await mh.updateMemberField(authorId, memberName, "name", trimmedName);
}
/**
@@ -257,9 +249,7 @@ mh.updateDisplayName = async function (authorId, membername, displayname) {
else if (trimmedName === '') {
throw new RangeError(`Display name ${enums.err.NO_VALUE}`);
}
return await mh.updateMemberField(authorId, membername, "displayname", trimmedName).catch((e) => {
throw e
});
return await mh.updateMemberField(authorId, membername, "displayname", trimmedName);
}
/**
@@ -270,13 +260,12 @@ mh.updateDisplayName = async function (authorId, membername, displayname) {
* @param {string} memberName - The member to update
* @param {string} proxy - The proxy to set
* @returns {Promise<string> } A successful update.
* @throws {Error} When an empty proxy was provided, or a proxy exists.
*/
mh.updateProxy = async function (authorId, memberName, proxy) {
// Throws error if exists
await mh.checkIfProxyExists(authorId, proxy).catch((e) => { throw e; });
await mh.checkIfProxyExists(authorId, proxy);
return await mh.updateMemberField(authorId, memberName, "proxy", proxy).catch((e) => { throw e;});
return await mh.updateMemberField(authorId, memberName, "proxy", proxy);
}
/**
@@ -289,14 +278,13 @@ mh.updateProxy = async function (authorId, memberName, proxy) {
* @param {string | null} attachmentUrl - The attachment URL, if any
* @param {string | null} attachmentExpiration - The attachment expiry date, if any
* @returns {Promise<string>} A successful update.
* @throws {Error} When loading the profile picture from a URL doesn't work.
*/
mh.updatePropic = async function (authorId, memberName, values, attachmentUrl = null, attachmentExpiration = null) {
const imgUrl = values ?? attachmentUrl;
// Throws error if invalid
await utils.checkImageFormatValidity(imgUrl).catch((e) => { throw e });
return await mh.updateMemberField(authorId, memberName, "propic", imgUrl, attachmentExpiration).catch((e) => { throw e });
await utils.checkImageFormatValidity(imgUrl);
const expirationWarning = utils.setExpirationWarning(imgUrl, attachmentExpiration);
return await mh.updateMemberField(authorId, memberName, "propic", imgUrl, expirationWarning);
}
/**
@@ -309,17 +297,17 @@ mh.updatePropic = async function (authorId, memberName, values, attachmentUrl =
* @throws {Error} When there is no member to remove.
*/
mh.removeMember = async function (authorId, memberName) {
return await database.members.destroy({
const destroyed = await database.members.destroy({
where: {
name: {[Op.iLike]: memberName},
userid: authorId
}
}).then((result) => {
if (result) {
return `Member "${memberName}" has been deleted.`;
}
throw new Error(`${enums.err.NO_MEMBER}`);
})
if (destroyed > 0) {
return `Member "${memberName}" has been deleted.`;
} else {
throw new Error(`${enums.err.NO_MEMBER}`);
}
}
/*======Non-Subcommands======*/
@@ -338,11 +326,10 @@ mh.removeMember = async function (authorId, memberName) {
* @throws {Error} When the member already exists, there are validation errors, or adding a member doesn't work.
*/
mh.addFullMember = async function (authorId, memberName, displayName = null, proxy = null, propic = null, attachmentExpiration = null) {
await mh.getMemberByName(authorId, memberName).then((member) => {
if (member) {
throw new Error(`Can't add ${memberName}. ${enums.err.MEMBER_EXISTS}`);
}
});
const existingMember = await mh.getMemberByName(authorId, memberName);
if (existingMember) {
throw new Error(`Can't add ${memberName}. ${enums.err.MEMBER_EXISTS}`);
}
const errors = [];
const trimmedName = memberName.trim();
@@ -366,28 +353,31 @@ mh.addFullMember = async function (authorId, memberName, displayName = null, pro
}
}
let isValidProxy;
if (proxy && proxy.length > 0) {
await mh.checkIfProxyExists(authorId, proxy).then(() => {
isValidProxy = true;
}).catch((e) => {
try {
const proxyExists = await mh.checkIfProxyExists(authorId, proxy);
isValidProxy = !proxyExists;
}
catch(e) {
errors.push(`Tried to set proxy to \"${proxy}\". ${e.message}. ${enums.err.SET_TO_NULL}`);
isValidProxy = false;
});
}
}
let isValidPropic;
if (propic && propic.length > 0) {
await utils.checkImageFormatValidity(propic).then(() => {
isValidPropic = true;
}).catch((e) => {
try {
isValidPropic = await utils.checkImageFormatValidity(propic);
}
catch(e) {
errors.push(`Tried to set profile picture to \"${propic}\". ${e.message}. ${enums.err.SET_TO_NULL}`);
isValidPropic = false;
});
}
}
if (isValidPropic && attachmentExpiration) {
errors.push(mh.setExpirationWarning(attachmentExpiration));
const expirationWarning = utils.setExpirationWarning(propic, attachmentExpiration);
if (expirationWarning) {
errors.push(expirationWarning);
}
const member = await database.members.create({
name: memberName, userid: authorId, displayname: isValidDisplayName ? displayName : null, proxy: isValidProxy ? proxy : null, propic: isValidPropic ? propic : null
@@ -404,42 +394,21 @@ mh.addFullMember = async function (authorId, memberName, displayName = null, pro
* @param {string} memberName - The member to update
* @param {string} columnName - The column name to update.
* @param {string} value - The value to update to.
* @param {string | null} [attachmentExpiration] - The attachment expiration date (if any)
* @param {string | null} [expirationWarning] - The attachment expiration warning (if any)
* @returns {Promise<string>} A successful update.
* @throws {Error} When no member row was updated.
*/
mh.updateMemberField = async function (authorId, memberName, columnName, value, attachmentExpiration = null) {
let fluxerPropicWarning;
// indicates that an attachment was uploaded on Fluxer directly
if (columnName === "propic" && attachmentExpiration) {
fluxerPropicWarning = mh.setExpirationWarning(value);
}
return await database.members.update({[columnName]: value}, {
mh.updateMemberField = async function (authorId, memberName, columnName, value, expirationWarning = null) {
const res = await database.members.update({[columnName]: value}, {
where: {
name: {[Op.iLike]: memberName},
userid: authorId
}
}).then((res) => {
if (res[0] === 0) {
throw new Error(`Can't update ${memberName}. ${enums.err.NO_MEMBER}.`);
} else {
return `Updated ${columnName} for ${memberName} to ${value}${fluxerPropicWarning ?? ''}.`;
}
})
}
/**
* Sets the warning for an expiration date.
*
* @param {string} expirationString - An expiration date string.
* @returns {string} A description of the expiration, interpolating the expiration string.
*/
mh.setExpirationWarning = function (expirationString) {
let expirationDate = new Date(expirationString);
if (!isNaN(expirationDate.valueOf())) {
expirationDate = expirationDate.toDateString();
return `\n**NOTE:** Because this profile picture was uploaded via Fluxer, it will currently expire on *${expirationDate}*. To avoid this, upload the picture to another website like <https://imgbb.com/> and link to it directly`
if (res[0] === 0) {
throw new Error(`Can't update ${memberName}. ${enums.err.NO_MEMBER}.`);
} else {
return `Updated ${columnName} for ${memberName} to ${value}${expirationWarning ? `. ${expirationWarning}.` : '.'}`;
}
}
@@ -472,12 +441,12 @@ mh.getMemberInfo = function (member) {
*/
mh.getAllMembersInfo = async function (authorId, authorName) {
const members = await mh.getMembersByAuthor(authorId);
if (members == null) throw Error(enums.err.USER_NO_MEMBERS);
const fields = [...members.entries()].map(([name, member]) => ({
if (members.length === 0) throw Error(enums.err.USER_NO_MEMBERS);
const fields = [...members.entries()].map(([index, member]) => ({
name: member.name, value: `(Proxy: \`${member.proxy ?? "unset"}\`)`, inline: true,
}));
return new EmbedBuilder()
.setTitle(`${fields > 25 ? "First 25 m" : "M"}embers for ${authorName}`)
.setTitle(`${fields.length > 25 ? "First 25 m" : "M"}embers for ${authorName}`)
.addFields(...fields);
}
@@ -517,14 +486,11 @@ mh.checkIfProxyExists = async function (authorId, proxy) {
if (splitProxy.length < 2) throw new Error(enums.err.NO_TEXT_FOR_PROXY);
if (!splitProxy[0] && !splitProxy[1]) throw new Error(enums.err.NO_PROXY_WRAPPER);
await mh.getMembersByAuthor(authorId).then((memberList) => {
const proxyExists = memberList.some(member => member.proxy === proxy);
if (proxyExists) {
throw new Error(enums.err.PROXY_EXISTS);
}
}).catch(e => {
throw e
});
const memberList = await mh.getMembersByAuthor(authorId);
const proxyExists = memberList.some(member => member.proxy === proxy);
if (proxyExists) {
throw new Error(enums.err.PROXY_EXISTS);
}
return false;
}