mirror of
https://github.com/pieartsy/PluralFlux.git
synced 2026-04-14 20:15:28 +10:00
Merge branch 'main' of https://github.com/pieartsy/PluralFlux into add-attachments
# Conflicts: # src/commands.js # src/enums.js # src/helpers/memberHelper.js # src/helpers/messageHelper.js # src/helpers/webhookHelper.js # tests/helpers/memberHelper.test.js # tests/helpers/messageHelper.test.js # tests/helpers/webhookHelper.test.js
This commit is contained in:
3
.github/FUNDING.yml
vendored
Normal file
3
.github/FUNDING.yml
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
github: pieartsy
|
||||
39
.github/workflows/node.js.yml
vendored
Normal file
39
.github/workflows/node.js.yml
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
# This workflow will do a clean installation of node dependencies, cache/restore them, build the source code and run tests across different versions of node
|
||||
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-nodejs
|
||||
|
||||
name: Node.js CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "main" ]
|
||||
pull_request:
|
||||
branches: [ "main" ]
|
||||
|
||||
jobs:
|
||||
|
||||
build:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [25.3.0]
|
||||
|
||||
steps:
|
||||
- name: Use Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
cache: 'npm'
|
||||
|
||||
- name: Run tests
|
||||
run: npm test
|
||||
working-directory: tests
|
||||
|
||||
- name: Tests failed
|
||||
if: failure()
|
||||
run: exit 1
|
||||
|
||||
- name: Tests passed
|
||||
run: npm run build --if-present
|
||||
working-directory: src
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,7 +1,6 @@
|
||||
node_modules
|
||||
.idea
|
||||
secrets/
|
||||
package-lock.json
|
||||
config.json
|
||||
coverage
|
||||
log.txt
|
||||
|
||||
21
README.md
21
README.md
@@ -5,6 +5,8 @@ PluralFlux is a proxybot akin to PluralKit and Tupperbox, but for [Fluxer](https
|
||||
|
||||
[Join the support server](https://fluxer.gg/WaO6qGdU)
|
||||
|
||||
[Sponsor the project](https://github.com/sponsors/pieartsy)
|
||||
|
||||
If it's not running at the moment, it's because my computer crashed or something. I'm looking to move running it to a somewhat more permanent solution.
|
||||
|
||||
## Commands
|
||||
@@ -12,11 +14,20 @@ All commands are prefixed by `pf;`. Currently only a few are implemented.
|
||||
|
||||
- `pf;help` - Sends the current list of commands.
|
||||
|
||||
- `pf;import` - Imports from PluralKit using the JSON file provided by their export command. Importing from other proxy bots is *TBD*. `pf;import` and attach your JSON file to the message. This will only save the fields that are present in the bot currently (the stuff above), not anything else like birthdays or system handles (yet?).
|
||||
- `pf;import` - Imports from PluralKit using the JSON file provided by their export command. Importing from other proxy bots is *TBD*. `pf;import` and attach your JSON file to the message. This will only save the fields that are present in the bot currently, not anything else like birthdays or system handles (yet?). **Only one proxy can be set per member currently.**"
|
||||
|
||||
- `pf;member` - Accesses the sub-commands related to editing proxy members. The available subcommands are:
|
||||
- `new` - Creates a new member to proxy with, for example: `pf;member new jane`. The member name should ideally be short so you can write other commands with it easily.
|
||||
You can optionally add a display name after the member name, for example: `pf;member new jane "Jane Doe | ze/hir"`. If it has spaces, put it in __double quotes__. The length limit is 32 characters.
|
||||
- `new` - Creates a new member to proxy with, for example: `pf;member new jane`. The member name should ideally be short so you can write other commands with it easily. The order of values is `pf;member new [name] [displayname] [proxy] [propic]`, _without brackets_. The name is **required**, but the rest are optional.
|
||||
Usage notes:
|
||||
- If anything has spaces, put it in quotes: `"Jane Doe"`
|
||||
- If anything is unset, and you want to set something after it (for ex: you haven't set a display name, but you want to add a proxy), put the unset value in empty quotes in the same position: "" If you leave it out, the bot will set things wrong.
|
||||
- The maximum length of a display name is 32 characters.
|
||||
- You can't use the same proxy for two different members.
|
||||
- You can also upload an image directly instead of using a url.
|
||||
Examples:
|
||||
- Full example: `pf;member new jane "Jane Doe" J:text https://cdn.pixabay.com/photo/2023/10/20/19/07/aster-8330078_1280.jpg`
|
||||
- Example with gaps: `pf;member new bob "Bob he/him" "" https://cdn.pixabay.com/photo/2016/05/09/11/09/tennis-1381230_1280.jpg
|
||||
|
||||
- `remove` - Removes a member based on their name, for example: `pf;member remove jane`.
|
||||
- `name` - Updates the name for a specific member based on their current name, for ex: `pf;member john name jane`. The member name should ideally be short so you can write other commands with it easily.
|
||||
- `list` - Lists all members in the system.
|
||||
@@ -25,10 +36,10 @@ You can optionally add a display name after the member name, for example: `pf;me
|
||||
1. Pass in a direct remote image URL, for example: `pf;member jane propic <https://cdn.pixabay.com/photo/2020/05/02/02/54/animal-5119676_1280.jpg>`. You can upload images on sites like <https://imgbb.com/>.
|
||||
2. Upload an attachment directly.
|
||||
**NOTE:** Fluxer does not save your attachments forever, so option #1 is recommended.
|
||||
- `proxy` Updates the proxy tag for a specific member based on their name. The proxy must be formatted with the tags surrounding the word 'text', for example: `pf;member jane proxy Jane:text` or `pf;member amal proxy [text]` This is so the bot can detect what the proxy tags are. Only one proxy can be set per member currently.
|
||||
- `proxy` Updates the proxy tag for a specific member based on their name. The proxy must be formatted with the tags surrounding the word 'text', for example: `pf;member jane proxy Jane:text` or `pf;member amal proxy [text]` This is so the bot can detect what the proxy tags are. **Only one proxy can be set per member currently.**
|
||||
|
||||
## Notes
|
||||
- Attaching files to messages with the proxy does not work, due to either the limitations of Fluxer itself :(
|
||||
- Only one proxy tag can be set per member currently.
|
||||
|
||||
## Upcoming
|
||||
- [ ] React with x to delete message
|
||||
|
||||
7758
package-lock.json
generated
Normal file
7758
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -34,7 +34,7 @@ client.on(Events.MessageCreate, async (message) => {
|
||||
|
||||
const commandName = content.slice(messageHelper.prefix.length).split(" ")[0];
|
||||
// If there's no command name (ie just the prefix)
|
||||
if (!commandName) await message.reply(enums.help.SHORT_DESC_PLURALFLUX);
|
||||
if (!commandName) return await message.reply(enums.help.SHORT_DESC_PLURALFLUX);
|
||||
|
||||
const args = messageHelper.parseCommandArgs(content, commandName);
|
||||
|
||||
@@ -44,10 +44,13 @@ client.on(Events.MessageCreate, async (message) => {
|
||||
throw e
|
||||
});
|
||||
}
|
||||
else {
|
||||
await message.reply(enums.err.COMMAND_NOT_RECOGNIZED);
|
||||
}
|
||||
}
|
||||
catch(error) {
|
||||
console.error(error);
|
||||
return await message.reply(error.message);
|
||||
// return await message.reply(error.message);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -12,13 +12,17 @@ cmds.set('member', {
|
||||
const authorFull = `${message.author.username}#${message.author.discriminator}`
|
||||
const attachmentUrl = message.attachments.size > 0 ? message.attachments.first().url : null;
|
||||
const attachmentExpires = message.attachments.size > 0 ? message.attachments.first().expires_at : null;
|
||||
const reply = await memberHelper.parseMemberCommand(message.author.id, authorFull, args, attachmentUrl, attachmentExpires).catch(e =>{throw e});
|
||||
const reply = await memberHelper.parseMemberCommand(message.author.id, authorFull, args, attachmentUrl, attachmentExpires).catch(async (e) =>{await message.reply(e.message);});
|
||||
if (typeof reply === 'string') {
|
||||
return await message.reply(reply);
|
||||
}
|
||||
else if (reply instanceof EmbedBuilder) {
|
||||
await message.reply({embeds: [reply.toJSON()]})
|
||||
}
|
||||
else if (typeof reply === 'object') {
|
||||
const errorsText = reply.errors.length > 0 ? reply.errors.join('\n- ') : null;
|
||||
return await message.reply({content: `${reply.success} ${errorsText ? "\nThese errors occurred:\n" + errorsText : ""}`, embeds: [reply.embed.toJSON()]})
|
||||
}
|
||||
|
||||
}
|
||||
})
|
||||
@@ -45,12 +49,11 @@ cmds.set('help', {
|
||||
|
||||
cmds.set('import', {
|
||||
description: enums.help.SHORT_DESC_IMPORT,
|
||||
async execute(message) {
|
||||
if (message.content.includes('--help')) {
|
||||
async execute(message, client, args) {
|
||||
const attachmentUrl = message.attachments.size > 0 ? message.attachments.first().url : null;
|
||||
if ((message.content.includes('--help') || (args[0] === '' && args.length === 1)) && !attachmentUrl ) {
|
||||
return await message.reply(enums.help.IMPORT);
|
||||
}
|
||||
const attachmentUrl = message.attachments.size > 0 ? message.attachments.first().url : null;
|
||||
|
||||
return await importHelper.pluralKitImport(message.author.id, attachmentUrl).then(async (successfullyAdded) => {
|
||||
await message.reply(successfullyAdded);
|
||||
}).catch(async (error) => {
|
||||
@@ -59,7 +62,7 @@ cmds.set('import', {
|
||||
let errorsText = `${error.message}.\nThese errors occurred:\n${error.errors.join('\n')}`;
|
||||
|
||||
await message.reply(errorsText).catch(async () => {
|
||||
const returnedBuffer = await messageHelper.returnBufferFromText(errorsText);
|
||||
const returnedBuffer = messageHelper.returnBufferFromText(errorsText);
|
||||
await message.reply({content: returnedBuffer.text, files: [{ name: 'text.pdf', data: returnedBuffer.file }]
|
||||
})
|
||||
});
|
||||
|
||||
10
src/enums.js
10
src/enums.js
@@ -7,7 +7,7 @@ helperEnums.err = {
|
||||
ADD_ERROR: "Error adding member.",
|
||||
MEMBER_EXISTS: "A member with that name already exists. Please pick a unique name.",
|
||||
USER_NO_MEMBERS: "You have no members created.",
|
||||
DISPLAY_NAME_TOO_LONG: "The display name is too long. Please limit it to 32 characters or less.",
|
||||
DISPLAY_NAME_TOO_LONG: "The maximum length of a display name is 32 characters.",
|
||||
PROXY_EXISTS: "A duplicate proxy already exists for one of your members. Please pick a new one, or change the old one first.",
|
||||
NO_SUCH_COMMAND: "No such command exists.",
|
||||
PROPIC_FAILS_REQUIREMENTS: "Profile picture must be in JPG, PNG, or WEBP format and less than 10MB.",
|
||||
@@ -20,6 +20,8 @@ helperEnums.err = {
|
||||
NOT_JSON_FILE: "Please attach a valid JSON file.",
|
||||
NO_MEMBERS_IMPORTED: 'No members were imported.',
|
||||
IMPORT_ERROR: "Please see attached file for logs on the member import process.",
|
||||
COMMAND_NOT_RECOGNIZED: "Command not recognized. Try typing `pf;help` for command list.",
|
||||
SET_TO_NULL: "It has been set to null instead."
|
||||
}
|
||||
|
||||
helperEnums.help = {
|
||||
@@ -29,14 +31,14 @@ helperEnums.help = {
|
||||
SHORT_DESC_PLURALFLUX: "PluralFlux is a proxybot akin to PluralKit and Tupperbot, but for Fluxer. All commands are prefixed by `pf;`. Type `pf;help` for info on the bot itself.",
|
||||
PLURALFLUX: "PluralFlux is a proxybot akin to PluralKit and Tupperbot, but for Fluxer. All commands are prefixed by `pf;`. Add ` --help` to the end of a command to find out more about it, or just send it without arguments.",
|
||||
MEMBER: "Accesses the sub-commands related to editing proxy members. The available subcommands are `list`, `new`, `remove`, `displayname`, `proxy`, and `propic`. Add ` --help` to the end of a subcommand to find out more about it, or just send it without arguments.",
|
||||
NEW: "Creates a new member to proxy with, for example: `pf;member new jane`. The member name should ideally be short so you can write other commands with it easily. \n\nYou can optionally add a display name after the member name, for example: `pf;member new jane \"Jane Doe | ze/hir\"`. If it has spaces, put it in __double quotes__. The length limit is 32 characters.",
|
||||
NEW: "Creates a new member to proxy with, for example: `pf;member new jane`. The member name should ideally be short so you can write other commands with it easily. \n\nThe order of values is `pf;member new [name] [displayname] [proxy] [propic]`, _without brackets_. The name is **required**, but the rest are optional.\nUsage notes:\n- If anything has spaces, put it in quotes.\n- If anything is unset and you want to set something after it (for ex: you haven't set a display name but you want to add a proxy), put the unset value in empty quotes in the same position: \"\" If you leave it out, the bot will set things wrong.\n- The maximum length of a display name is 32 characters.\n- You can't use the same proxy for two different members.\n- You can also upload an image directly instead of using a url.\nExamples:\n- Everything filled out: `pf;member new jane \"Jane Doe\" J:text https://cdn.pixabay.com/photo/2023/10/20/19/07/aster-8330078_1280.jpg`\n- Example with gaps: `pf;member new bob \"Bob he/him\" \"\" https://cdn.pixabay.com/photo/2016/05/09/11/09/tennis-1381230_1280.jpg`",
|
||||
REMOVE: "Removes a member based on their name, for example: `pf;member remove jane`.",
|
||||
LIST: "Lists members in the system. Currently only lists the first 25.",
|
||||
NAME: "Updates the name for a specific member based on their current name, for ex: `pf;member john name jane`. The member name should ideally be short so you can write other commands with it easily.",
|
||||
DISPLAY_NAME: "Updates the display name for a specific member based on their name, for example: `pf;member jane \"Jane Doe | ze/hir\"`.This can be up to 32 characters long. If it has spaces, put it in __double quotes__.",
|
||||
PROXY: "Updates the proxy tag for a specific member based on their name. The proxy must be formatted with the tags surrounding the word 'text', for example: `pf;member jane proxy Jane:text` or `pf;member amal proxy [text]` This is so the bot can detect what the proxy tags are. Only one proxy can be set per member currently.",
|
||||
PROXY: "Updates the proxy tag for a specific member based on their name. The proxy must be formatted with the tags surrounding the word 'text', for example: `pf;member jane proxy Jane:text` or `pf;member amal proxy [text]` This is so the bot can detect what the proxy tags are. **Only one proxy can be set per member currently.**",
|
||||
PROPIC: "Updates the profile picture for the member. Must be in JPG, PNG, or WEBP format and less than 10MB. The two options are:\n1. Pass in a direct remote image URL, for example: `pf;member jane propic https://cdn.pixabay.com/photo/2020/05/02/02/54/animal-5119676_1280.jpg`. You can upload images on sites like https://imgbb.com/.\n2. Upload an attachment directly.\n\n**NOTE:** Fluxer does not save your attachments forever, so option #1 is recommended.",
|
||||
IMPORT: "Imports from PluralKit using the JSON file provided by their export command. Importing from other proxy bots is TBD. `pf;import` and attach your JSON file to the message. This will only save the fields that are present in the bot currently, not anything else like birthdays or system handles (yet?)."
|
||||
IMPORT: "Imports from PluralKit using the JSON file provided by their export command. Importing from other proxy bots is TBD. `pf;import` and attach your JSON file to the message. This will only save the fields that are present in the bot currently, not anything else like birthdays or system handles (yet?). **Only one proxy can be set per member currently.**"
|
||||
}
|
||||
|
||||
helperEnums.misc = {
|
||||
|
||||
@@ -18,17 +18,19 @@ ih.pluralKitImport = async function (authorId, attachmentUrl) {
|
||||
}
|
||||
return fetch(attachmentUrl).then((res) => res.json()).then(async(pkData) => {
|
||||
const pkMembers = pkData.members;
|
||||
const errors = [];
|
||||
let errors = [];
|
||||
const addedMembers = [];
|
||||
for (let pkMember of pkMembers) {
|
||||
const proxy = pkMember.proxy_tags[0] ? `${pkMember.proxy_tags[0].prefix ?? ''}text${pkMember.proxy_tags[0].suffix ?? ''}` : null;
|
||||
await memberHelper.addFullMember(authorId, pkMember.name, pkMember.display_name, proxy, pkMember.avatar_url, true).then((member) => {
|
||||
addedMembers.push(member.name);
|
||||
await memberHelper.addFullMember(authorId, pkMember.name, pkMember.display_name, proxy, pkMember.avatar_url).then((memberObj) => {
|
||||
addedMembers.push(memberObj.member.name);
|
||||
if (memberObj.errors.length > 0) {
|
||||
errors.push(`\n**${pkMember.name}:** `)
|
||||
errors = errors.concat(memberObj.errors);
|
||||
}
|
||||
}).catch(e => {
|
||||
errors.push(`${pkMember.name}: ${e.message}`);
|
||||
errors.push(e.message);
|
||||
});
|
||||
await memberHelper.checkImageFormatValidity(pkMember.avatar_url).catch(e => {
|
||||
errors.push(`${pkMember.name}: ${e.message}`)});
|
||||
}
|
||||
const aggregatedText = addedMembers.length > 0 ? `Successfully added members: ${addedMembers.join(', ')}` : enums.err.NO_MEMBERS_IMPORTED;
|
||||
if (errors.length > 0) {
|
||||
|
||||
@@ -17,7 +17,9 @@ const commandList = ['--help', 'new', 'remove', 'name', 'list', 'displayName', '
|
||||
* @param {string[]} args - The message arguments
|
||||
* @param {string | null} attachmentUrl - The message attachment url.
|
||||
* @param {string | null} attachmentExpiration - The message attachment expiration (if uploaded via Fluxer)
|
||||
* @returns {Promise<string> | Promise <EmbedBuilder>} A message, or an informational embed.
|
||||
* @returns {Promise<string>} A success message.
|
||||
* @returns {Promise <EmbedBuilder>} A list of 25 members as an embed.
|
||||
* @returns {Promise<{EmbedBuilder, [], string}>} A member info embed + info/errors.
|
||||
* @throws {Error}
|
||||
*/
|
||||
mh.parseMemberCommand = async function (authorId, authorFull, args, attachmentUrl = null, attachmentExpiration = null) {
|
||||
@@ -30,7 +32,7 @@ mh.parseMemberCommand = async function (authorId, authorFull, args, attachmentUr
|
||||
case '--help':
|
||||
return enums.help.MEMBER;
|
||||
case 'new':
|
||||
return await mh.addNewMember(authorId, args).catch((e) => {
|
||||
return await mh.addNewMember(authorId, args, attachmentUrl).catch((e) => {
|
||||
throw e
|
||||
});
|
||||
case 'remove':
|
||||
@@ -86,20 +88,22 @@ mh.parseMemberCommand = async function (authorId, authorFull, args, attachmentUr
|
||||
* @async
|
||||
* @param {string} authorId - The author of the message
|
||||
* @param {string[]} args - The message arguments
|
||||
* @param {string | null} attachmentURL - The attachment URL, if any exists
|
||||
* @returns {Promise<string>} A successful addition.
|
||||
* @throws {Error} When the member exists, or creating a member doesn't work.
|
||||
*/
|
||||
mh.addNewMember = async function (authorId, args) {
|
||||
mh.addNewMember = async function (authorId, args, attachmentURL = null) {
|
||||
if (args[1] && args[1] === "--help" || !args[1]) {
|
||||
return enums.help.NEW;
|
||||
}
|
||||
const memberName = args[1];
|
||||
const displayName = args[2];
|
||||
const proxy = args[3];
|
||||
const propic = args[4] ?? attachmentURL;
|
||||
|
||||
return await mh.addFullMember(authorId, memberName, displayName).then((member) => {
|
||||
let success = `Member was successfully added.\nName: ${member.name}`
|
||||
success += displayName ? `\nDisplay name: ${member.displayname}` : "";
|
||||
return success;
|
||||
return await mh.addFullMember(authorId, memberName, displayName, proxy, propic).then(async(response) => {
|
||||
const memberInfoEmbed = await mh.getMemberInfo(authorId, memberName).catch((e) => {throw e})
|
||||
return {embed: memberInfoEmbed, errors: response.errors, success: `${memberName} has been added successfully.`};
|
||||
}).catch(e => {
|
||||
throw e;
|
||||
})
|
||||
@@ -287,48 +291,157 @@ mh.removeMember = async function (authorId, args) {
|
||||
* @param {string | null} displayName - The display name of the member.
|
||||
* @param {string | null} proxy - The proxy tag of the member.
|
||||
* @param {string | null} propic - The profile picture URL of the member.
|
||||
* @param {boolean} isImport - Whether calling from the import function or not.
|
||||
* @returns {Promise<model>} A successful addition.
|
||||
* @throws {Error | RangeError} When the member already exists, there are validation errors, or adding a member doesn't work.
|
||||
* @returns {Promise<{model, []}>} A successful addition object, including errors if there are any.
|
||||
* @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, isImport = false) {
|
||||
mh.addFullMember = async function (authorId, memberName, displayName = null, proxy = null, propic = null) {
|
||||
await mh.getMemberByName(authorId, memberName).then((member) => {
|
||||
if (member) {
|
||||
throw new Error(`Can't add ${memberName}. ${enums.err.MEMBER_EXISTS}`);
|
||||
}
|
||||
});
|
||||
if (displayName) {
|
||||
const errors = [];
|
||||
|
||||
let isValidDisplayName;
|
||||
if (displayName && displayName.length > 0) {
|
||||
const trimmedName = displayName ? displayName.trim() : null;
|
||||
if (trimmedName && trimmedName.length > 32) {
|
||||
throw new RangeError(`Can't add ${memberName}. ${enums.err.DISPLAY_NAME_TOO_LONG}`);
|
||||
errors.push(`Tried to set displayname to \"${displayName}\". ${enums.err.DISPLAY_NAME_TOO_LONG}. ${enums.err.SET_TO_NULL}`);
|
||||
isValidDisplayName = false;
|
||||
}
|
||||
else {
|
||||
isValidDisplayName = true;
|
||||
}
|
||||
}
|
||||
if (proxy) {
|
||||
await mh.checkIfProxyExists(authorId, proxy).catch((e) => {
|
||||
throw e
|
||||
});
|
||||
}
|
||||
let validPropic;
|
||||
if (propic) {
|
||||
validPropic = await mh.checkImageFormatValidity(propic).then((valid) => {
|
||||
return valid;
|
||||
|
||||
let isValidProxy;
|
||||
if (proxy && proxy.length > 0) {
|
||||
await mh.checkIfProxyExists(authorId, proxy).then(() => {
|
||||
isValidProxy = true;
|
||||
}).catch((e) => {
|
||||
if (!isImport) {
|
||||
throw (e);
|
||||
}
|
||||
return false;
|
||||
errors.push(`Tried to set proxy to \"${proxy}\". ${e.message}. ${enums.err.SET_TO_NULL}`);
|
||||
isValidProxy = false;
|
||||
});
|
||||
}
|
||||
|
||||
const member = await database.members.create({
|
||||
name: memberName, userid: authorId, displayname: displayName, proxy: proxy, propic: validPropic ? propic : null,
|
||||
let isValidPropic;
|
||||
if (propic && propic.length > 0) {
|
||||
await mh.checkImageFormatValidity(propic).then(() => {
|
||||
isValidPropic = true;
|
||||
}).catch((e) => {
|
||||
errors.push(`Tried to set profile picture to \"${propic}\". ${e.message}. ${enums.err.SET_TO_NULL}`);
|
||||
isValidPropic = false;
|
||||
});
|
||||
if (!member) {
|
||||
new Error(`${enums.err.ADD_ERROR}`);
|
||||
}
|
||||
return member;
|
||||
const member = await database.members.create({
|
||||
name: memberName, userid: authorId, displayname: isValidDisplayName ? displayName : null, proxy: isValidProxy ? proxy : null, propic: isValidPropic ? propic : null
|
||||
});
|
||||
|
||||
return {member: member, errors: errors};
|
||||
}
|
||||
|
||||
// mh.mergeFullMember = async function (authorId, memberName, displayName = null, proxy = null, propic = null) {
|
||||
// await mh.getMemberByName(authorId, memberName).then((member) => {
|
||||
// if (member) {
|
||||
// throw new Error(`Can't add ${memberName}. ${enums.err.MEMBER_EXISTS}`);
|
||||
// }
|
||||
// });
|
||||
//
|
||||
// let isValidDisplayName;
|
||||
// if (displayName) {
|
||||
// const trimmedName = displayName ? displayName.trim() : null;
|
||||
// if (trimmedName && trimmedName.length > 32) {
|
||||
// if (!isImport) {
|
||||
// throw new RangeError(`Can't add ${memberName}. ${enums.err.DISPLAY_NAME_TOO_LONG}`);
|
||||
// }
|
||||
// isValidDisplayName = false;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// let isValidProxy;
|
||||
// if (proxy) {
|
||||
// isValidProxy = await mh.checkIfProxyExists(authorId, proxy).then((res) => {
|
||||
// return res;
|
||||
// }).catch((e) => {
|
||||
// if (!isImport) {
|
||||
// throw e
|
||||
// }
|
||||
// return false;
|
||||
// });
|
||||
// }
|
||||
//
|
||||
// let isValidPropic;
|
||||
// if (propic) {
|
||||
// isValidPropic = await mh.checkImageFormatValidity(propic).then((valid) => {
|
||||
// return valid;
|
||||
// }).catch((e) => {
|
||||
// if (!isImport) {
|
||||
// throw (e);
|
||||
// }
|
||||
// return false;
|
||||
// });
|
||||
// }
|
||||
//
|
||||
// const member = await database.members.create({
|
||||
// name: memberName, userid: authorId, displayname: isValidDisplayName ? displayName: null, proxy: isValidProxy ? proxy : null, propic: isValidPropic ? propic : null,
|
||||
// });
|
||||
// if (!member) {
|
||||
// new Error(`${enums.err.ADD_ERROR}`);
|
||||
// }
|
||||
// return member;
|
||||
// }
|
||||
//
|
||||
// mh.overwriteFullMemberFromImport = async function (authorId, memberName, displayName = null, proxy = null, propic = null) {
|
||||
// await mh.getMemberByName(authorId, memberName).then((member) => {
|
||||
// if (member) {
|
||||
// throw new Error(`Can't add ${memberName}. ${enums.err.MEMBER_EXISTS}`);
|
||||
// }
|
||||
// });
|
||||
//
|
||||
// let isValidDisplayName;
|
||||
// if (displayName) {
|
||||
// const trimmedName = displayName ? displayName.trim() : null;
|
||||
// if (trimmedName && trimmedName.length > 32) {
|
||||
// if (!isImport) {
|
||||
// throw new RangeError(`Can't add ${memberName}. ${enums.err.DISPLAY_NAME_TOO_LONG}`);
|
||||
// }
|
||||
// isValidDisplayName = false;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// let isValidProxy;
|
||||
// if (proxy) {
|
||||
// isValidProxy = await mh.checkIfProxyExists(authorId, proxy).then((res) => {
|
||||
// return res;
|
||||
// }).catch((e) => {
|
||||
// if (!isImport) {
|
||||
// throw e
|
||||
// }
|
||||
// return false;
|
||||
// });
|
||||
// }
|
||||
//
|
||||
// let isValidPropic;
|
||||
// if (propic) {
|
||||
// isValidPropic = await mh.checkImageFormatValidity(propic).then((valid) => {
|
||||
// return valid;
|
||||
// }).catch((e) => {
|
||||
// if (!isImport) {
|
||||
// throw (e);
|
||||
// }
|
||||
// return false;
|
||||
// });
|
||||
// }
|
||||
//
|
||||
// const member = await database.members.create({
|
||||
// name: memberName, userid: authorId, displayname: isValidDisplayName ? displayName: null, proxy: isValidProxy ? proxy : null, propic: isValidPropic ? propic : null,
|
||||
// });
|
||||
// if (!member) {
|
||||
// new Error(`${enums.err.ADD_ERROR}`);
|
||||
// }
|
||||
// return member;
|
||||
// }
|
||||
|
||||
/**
|
||||
* Updates one fields for a member in the database.
|
||||
*
|
||||
|
||||
@@ -119,7 +119,7 @@ function retryPromise(promise, options) {
|
||||
/**
|
||||
* Returns an ArrayBuffer from an attachment URL.
|
||||
*
|
||||
* @param {Map} attachments - A collection of attachments from the message object
|
||||
* @param {Collection<string, APIMessageAttachment>} attachments - A collection of attachments from the message object
|
||||
* @returns {[{string, ArrayBuffer}]} An array of file objects
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -24,7 +24,8 @@ wh.sendMessageAsMember = async function(client, message) {
|
||||
if (!message.guildId) {
|
||||
throw new Error(enums.err.NOT_IN_SERVER);
|
||||
}
|
||||
const attachments = await messageHelper.createFileObjectFromAttachments(message.attachments);
|
||||
const attachments = messageHelper.createFileObjectFromAttachments(message.attachments);
|
||||
|
||||
await wh.replaceMessage(client, message, proxyMatch.message, proxyMatch.member, attachments).catch(e =>{throw e});
|
||||
}
|
||||
|
||||
|
||||
@@ -1,13 +1,24 @@
|
||||
jest.mock('@fluxerjs/core', () => jest.fn());
|
||||
jest.mock('../../src/database.js', () => jest.fn());
|
||||
jest.mock('sequelize', () => jest.fn());
|
||||
|
||||
const {EmbedBuilder} = require("@fluxerjs/core");
|
||||
const {database} = require('../../src/database.js');
|
||||
const {enums} = require('../../src/enums.js');
|
||||
const {EmptyResultError, Op} = require('sequelize');
|
||||
const {memberHelper} = require("../../src/helpers/memberHelper.js");
|
||||
|
||||
jest.mock('@fluxerjs/core', () => jest.fn());
|
||||
jest.mock('../../src/database.js', () => {
|
||||
return {
|
||||
database: {
|
||||
members: {
|
||||
create: jest.fn().mockResolvedValue(),
|
||||
update: jest.fn().mockResolvedValue(),
|
||||
destroy: jest.fn().mockResolvedValue(),
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
jest.mock('sequelize', () => jest.fn());
|
||||
|
||||
describe('MemberHelper', () => {
|
||||
const authorId = "0001";
|
||||
const authorFull = "author#0001";
|
||||
@@ -34,7 +45,6 @@ describe('MemberHelper', () => {
|
||||
});
|
||||
|
||||
test.each([
|
||||
[['new'], 'new member', 'addNewMember', ['new']],
|
||||
[['remove'], 'remove member', 'removeMember', ['remove']],
|
||||
[['list'], 'all member info', 'getAllMembersInfo', authorFull],
|
||||
[['somePerson', 'name'], 'update name', 'updateName', ['somePerson', 'name']],
|
||||
@@ -42,7 +52,7 @@ describe('MemberHelper', () => {
|
||||
[['somePerson', 'proxy'], 'get proxy', 'getProxyByMember', 'somePerson'],
|
||||
[['somePerson', 'proxy', 'test'], 'update proxy', 'updateProxy', ['somePerson', 'proxy', 'test']],
|
||||
[['somePerson'], 'member info', 'getMemberInfo', 'somePerson'],
|
||||
])('%s calls methods and returns correct values', async (args, expectedResult, method, passedIn) => {
|
||||
])('%s calls %s and returns correct values', async (args, expectedResult, method, passedIn) => {
|
||||
// Act
|
||||
return memberHelper.parseMemberCommand(authorId, authorFull, args).then((result) => {
|
||||
// Assert
|
||||
@@ -52,7 +62,21 @@ describe('MemberHelper', () => {
|
||||
});
|
||||
});
|
||||
|
||||
test('["somePerson", "propic"] returns correct values and calls methods', () => {
|
||||
|
||||
test.each([
|
||||
[['new'], attachmentUrl],
|
||||
[['new'], null,]
|
||||
])('%s returns correct values and calls addNewMember', (args, attachmentUrl) => {
|
||||
// Act
|
||||
return memberHelper.parseMemberCommand(authorId, authorFull, args, attachmentUrl).then((result) => {
|
||||
// Assert
|
||||
expect(result).toEqual("new member");
|
||||
expect(memberHelper.addNewMember).toHaveBeenCalledTimes(1);
|
||||
expect(memberHelper.addNewMember).toHaveBeenCalledWith(authorId, args, attachmentUrl);
|
||||
});
|
||||
})
|
||||
|
||||
test('["somePerson", "propic"] returns correct values and updatePropic', () => {
|
||||
// Arrange
|
||||
const args = ['somePerson', 'propic'];
|
||||
// Act
|
||||
@@ -98,7 +122,6 @@ describe('MemberHelper', () => {
|
||||
jest.spyOn(memberHelper, 'getProxyByMember').mockImplementation(() => { throw new Error('get proxy error')});
|
||||
})
|
||||
test.each([
|
||||
[['new'], 'new member error', 'addNewMember', ['new']],
|
||||
[['remove'], 'remove member error', 'removeMember', ['remove']],
|
||||
[['list'], 'all member info error', 'getAllMembersInfo', authorFull],
|
||||
[['somePerson', 'name'], 'update name error', 'updateName', ['somePerson', 'name']],
|
||||
@@ -115,6 +138,31 @@ describe('MemberHelper', () => {
|
||||
expect(memberHelper[method]).toHaveBeenCalledWith(authorId, passedIn)
|
||||
});
|
||||
});
|
||||
|
||||
test.each([
|
||||
[['new'], attachmentUrl],
|
||||
[['new'], null,]
|
||||
])('%s throws correct error when addNewMember returns error', (args, attachmentUrl) => {
|
||||
// Act
|
||||
return memberHelper.parseMemberCommand(authorId, authorFull, args, attachmentUrl).catch((result) => {
|
||||
// Assert
|
||||
expect(result).toEqual(new Error("new member error"));
|
||||
expect(memberHelper.addNewMember).toHaveBeenCalledTimes(1);
|
||||
expect(memberHelper.addNewMember).toHaveBeenCalledWith(authorId, args, attachmentUrl);
|
||||
});
|
||||
})
|
||||
|
||||
test('["somePerson", "propic"] throws correct error when updatePropic returns error', () => {
|
||||
// Arrange
|
||||
const args = ['somePerson', 'propic'];
|
||||
// Act
|
||||
return memberHelper.parseMemberCommand(authorId, authorFull, args, attachmentUrl, attachmentExpiration).catch((result) => {
|
||||
// Assert
|
||||
expect(result).toEqual(new Error("update propic error"));
|
||||
expect(memberHelper['updatePropic']).toHaveBeenCalledTimes(1);
|
||||
expect(memberHelper['updatePropic']).toHaveBeenCalledWith(authorId, args, attachmentUrl, attachmentExpiration)
|
||||
});
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -131,29 +179,31 @@ describe('MemberHelper', () => {
|
||||
})
|
||||
})
|
||||
|
||||
test('returns member without display name when name passed in', async () => {
|
||||
test('calls getMemberInfo when successful and returns result', async () => {
|
||||
// Arrange
|
||||
const args = ['new', 'some person'];
|
||||
const memberObject = { name: args[1] }
|
||||
const expected = "Member was successfully added.\nName: " + args[1];
|
||||
jest.spyOn(memberHelper, 'addFullMember').mockResolvedValue(memberObject);
|
||||
jest.spyOn(memberHelper, 'getMemberInfo').mockResolvedValue(memberObject);
|
||||
//Act
|
||||
return memberHelper.addNewMember(authorId, args).then((result) => {
|
||||
// Assert
|
||||
expect(result).toEqual(expected);
|
||||
expect(result).toEqual(memberObject);
|
||||
expect(memberHelper.getMemberInfo).toHaveBeenCalledTimes(1);
|
||||
expect(memberHelper.getMemberInfo).toHaveBeenCalledWith(authorId, args[1]);
|
||||
})
|
||||
})
|
||||
|
||||
test('returns member with display name when name and display name passed in', async () => {
|
||||
test('throws expected error when getMemberInfo throws error', async () => {
|
||||
// Arrange
|
||||
const args = ['new', 'some person', 'Some person Full Name'];
|
||||
const memberObject = { name: args[1], displayname: args[2] }
|
||||
const expected = "Member was successfully added.\nName: " + args[1] + "\nDisplay name: " + args[2];
|
||||
const args = ['new', 'some person'];
|
||||
const memberObject = { name: args[1] }
|
||||
jest.spyOn(memberHelper, 'addFullMember').mockResolvedValue(memberObject);
|
||||
jest.spyOn(memberHelper, 'getMemberInfo').mockImplementation(() => { throw new Error('getMemberInfo error') });
|
||||
//Act
|
||||
return memberHelper.addNewMember(authorId, args).then((result) => {
|
||||
return memberHelper.addNewMember(authorId, args).catch((result) => {
|
||||
// Assert
|
||||
expect(result).toEqual(expected);
|
||||
expect(result).toEqual(new Error('getMemberInfo error'));
|
||||
})
|
||||
})
|
||||
|
||||
@@ -322,6 +372,136 @@ describe('MemberHelper', () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe('addFullMember', () => {
|
||||
const memberName = "somePerson";
|
||||
const displayName = "Some Person";
|
||||
const proxy = "--text";
|
||||
const propic = "oya.png";
|
||||
beforeEach(() => {
|
||||
database.members.create = jest.fn().mockResolvedValue();
|
||||
jest.spyOn(memberHelper, 'getMemberByName').mockResolvedValue();
|
||||
})
|
||||
|
||||
test('calls getMemberByName', async() => {
|
||||
// Act
|
||||
return await memberHelper.addFullMember(authorId, memberName).then(() => {
|
||||
// Assert
|
||||
expect(memberHelper.getMemberByName).toHaveBeenCalledWith(authorId, memberName);
|
||||
expect(memberHelper.getMemberByName).toHaveBeenCalledTimes(1);
|
||||
})
|
||||
})
|
||||
|
||||
test('if getMemberByName returns member, throw error', async() => {
|
||||
memberHelper.getMemberByName.mockResolvedValue({name: memberName});
|
||||
// Act
|
||||
return await memberHelper.addFullMember(authorId, memberName).catch((e) => {
|
||||
// Assert
|
||||
expect(e).toEqual(new Error(`Can't add ${memberName}. ${enums.err.MEMBER_EXISTS}`))
|
||||
expect(database.members.create).not.toHaveBeenCalled();
|
||||
})
|
||||
})
|
||||
|
||||
test('if displayname is over 32 characters, call database.member.create with null value', async() => {
|
||||
// Arrange
|
||||
const displayName = "Some person with a very very very long name that can't be processed";
|
||||
const expectedMemberArgs = {name: memberName, userid: authorId, displayname: null, proxy: null, propic: null}
|
||||
database.members.create = jest.fn().mockResolvedValue(expectedMemberArgs);
|
||||
const expectedReturn = {member: expectedMemberArgs, errors: [`Tried to set displayname to \"${displayName}\". ${enums.err.DISPLAY_NAME_TOO_LONG}. ${enums.err.SET_TO_NULL}`]}
|
||||
|
||||
// Act
|
||||
return await memberHelper.addFullMember(authorId, memberName, displayName, null, null).then((res) => {
|
||||
// Assert
|
||||
expect(res).toEqual(expectedReturn);
|
||||
expect(database.members.create).toHaveBeenCalledWith(expectedMemberArgs);
|
||||
expect(database.members.create).toHaveBeenCalledTimes(1);
|
||||
})
|
||||
})
|
||||
|
||||
test('if proxy, call checkIfProxyExists', async() => {
|
||||
// Arrange
|
||||
jest.spyOn(memberHelper, 'checkIfProxyExists').mockResolvedValue();
|
||||
const expectedMemberArgs = {name: memberName, userid: authorId, displayname: null, proxy: proxy, propic: null}
|
||||
database.members.create = jest.fn().mockResolvedValue(expectedMemberArgs);
|
||||
const expectedReturn = {member: expectedMemberArgs, errors: []}
|
||||
|
||||
// Act
|
||||
return await memberHelper.addFullMember(authorId, memberName, null, proxy).then((res) => {
|
||||
// Assert
|
||||
expect(res).toEqual(expectedReturn);
|
||||
expect(memberHelper.checkIfProxyExists).toHaveBeenCalledWith(authorId, proxy);
|
||||
expect(memberHelper.checkIfProxyExists).toHaveBeenCalledTimes(1);
|
||||
expect(database.members.create).toHaveBeenCalledWith(expectedMemberArgs);
|
||||
expect(database.members.create).toHaveBeenCalledTimes(1);
|
||||
})
|
||||
})
|
||||
|
||||
test('if checkProxyExists throws error, call database.member.create with null value', async() => {
|
||||
// Arrange
|
||||
jest.spyOn(memberHelper, 'checkIfProxyExists').mockImplementation(() => {throw new Error('error')});
|
||||
const expectedMemberArgs = {name: memberName, userid: authorId, displayname: null, proxy: null, propic: null}
|
||||
database.members.create = jest.fn().mockResolvedValue(expectedMemberArgs);
|
||||
const expectedReturn = {member: expectedMemberArgs, errors: [`Tried to set proxy to \"${proxy}\". error. ${enums.err.SET_TO_NULL}`]}
|
||||
|
||||
// Act
|
||||
return await memberHelper.addFullMember(authorId, memberName, null, proxy, null).then((res) => {
|
||||
// Assert
|
||||
expect(res).toEqual(expectedReturn);
|
||||
expect(database.members.create).toHaveBeenCalledWith(expectedMemberArgs);
|
||||
expect(database.members.create).toHaveBeenCalledTimes(1);
|
||||
})
|
||||
})
|
||||
|
||||
test('if propic, call checkImageFormatValidity', async() => {
|
||||
// Arrange
|
||||
jest.spyOn(memberHelper, 'checkImageFormatValidity').mockResolvedValue();
|
||||
const expectedMemberArgs = {name: memberName, userid: authorId, displayname: null, proxy: null, propic: propic}
|
||||
database.members.create = jest.fn().mockResolvedValue(expectedMemberArgs);
|
||||
const expectedReturn = {member: expectedMemberArgs, errors: []}
|
||||
// Act
|
||||
return await memberHelper.addFullMember(authorId, memberName, null, null, propic).then((res) => {
|
||||
// Assert
|
||||
expect(res).toEqual(expectedReturn);
|
||||
expect(memberHelper.checkImageFormatValidity).toHaveBeenCalledWith(propic);
|
||||
expect(memberHelper.checkImageFormatValidity).toHaveBeenCalledTimes(1);
|
||||
expect(database.members.create).toHaveBeenCalledWith(expectedMemberArgs);
|
||||
expect(database.members.create).toHaveBeenCalledTimes(1);
|
||||
})
|
||||
})
|
||||
|
||||
test('if checkImageFormatValidity throws error, call database.member.create with null value', async() => {
|
||||
// Arrange
|
||||
jest.spyOn(memberHelper, 'checkImageFormatValidity').mockImplementation(() => {throw new Error('error')});
|
||||
const expectedMemberArgs = {name: memberName, userid: authorId, displayname: null, proxy: null, propic: null}
|
||||
database.members.create = jest.fn().mockResolvedValue(expectedMemberArgs);
|
||||
const expectedReturn = {member: expectedMemberArgs, errors: [`Tried to set profile picture to \"${propic}\". error. ${enums.err.SET_TO_NULL}`]}
|
||||
// Act
|
||||
return await memberHelper.addFullMember(authorId, memberName, null, null, propic).then((res) => {
|
||||
// Assert
|
||||
expect(res).toEqual(expectedReturn);
|
||||
expect(database.members.create).toHaveBeenCalledWith(expectedMemberArgs);
|
||||
expect(database.members.create).toHaveBeenCalledTimes(1);
|
||||
})
|
||||
})
|
||||
|
||||
test('if all values are valid, call database.members.create', async() => {
|
||||
// Arrange
|
||||
jest.spyOn(memberHelper, 'checkIfProxyExists').mockResolvedValue();
|
||||
jest.spyOn(memberHelper, 'checkImageFormatValidity').mockResolvedValue();
|
||||
const expectedMemberArgs = {name: memberName, userid: authorId, displayname: displayName, proxy: proxy, propic: propic}
|
||||
database.members.create = jest.fn().mockResolvedValue(expectedMemberArgs);
|
||||
const expectedReturn = {member: expectedMemberArgs, errors: []}
|
||||
// Act
|
||||
// Act
|
||||
return await memberHelper.addFullMember(authorId, memberName, displayName, proxy, propic).then((res) => {
|
||||
// Assert
|
||||
expect(res).toEqual(expectedReturn);
|
||||
expect(database.members.create).toHaveBeenCalledWith(expectedMemberArgs);
|
||||
expect(database.members.create).toHaveBeenCalledTimes(1);
|
||||
})
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
// restore the spy created with spyOn
|
||||
jest.restoreAllMocks();
|
||||
|
||||
@@ -2,6 +2,8 @@ const env = require('dotenv');
|
||||
env.config();
|
||||
|
||||
const {memberHelper} = require("../../src/helpers/memberHelper.js");
|
||||
const {Message} = require("@fluxerjs/core");
|
||||
const {fs} = require('fs');
|
||||
const {enums} = require('../../src/enums');
|
||||
const fetch = require('node-fetch');
|
||||
|
||||
@@ -43,10 +45,10 @@ describe('messageHelper', () => {
|
||||
const membersFor1 = [
|
||||
{name: "somePerson", proxy: "--text"},
|
||||
{name: "someSecondPerson", proxy: undefined},
|
||||
, {name: "someOtherPerson", proxy: "?text}"},
|
||||
{name: "someOtherPerson", proxy: "?text}"},
|
||||
{name: "someLastPerson", proxy: "{text}"},
|
||||
{name: "someEmojiPerson", proxy: "⭐text"},
|
||||
{name: "someSpacePerson", proxy: "-- text"},
|
||||
{name: "someSpacePerson", proxy: "! text"},
|
||||
]
|
||||
|
||||
const membersFor2 = []
|
||||
@@ -71,10 +73,10 @@ describe('messageHelper', () => {
|
||||
['1', 'hello', attachmentUrl, {}],
|
||||
['1', '--hello', attachmentUrl, {member: membersFor1[0], message: 'hello', hasAttachment: true}],
|
||||
['1', '--', attachmentUrl, {member: membersFor1[0], message: '', hasAttachment: true}],
|
||||
['1', '?hello}', null, {member: membersFor1[3], message: 'hello', hasAttachment: false}],
|
||||
['1', '{hello}', null, {member: membersFor1[4], message: 'hello', hasAttachment: false}],
|
||||
['1', '⭐hello', null, {member: membersFor1[5], message: 'hello', hasAttachment: false}],
|
||||
['1', '-- hello', null, {member: membersFor1[5], message: 'hello', hasAttachment: false}]
|
||||
['1', '?hello}', null, {member: membersFor1[2], message: 'hello', hasAttachment: false}],
|
||||
['1', '{hello}', null, {member: membersFor1[3], message: 'hello', hasAttachment: false}],
|
||||
['1', '⭐hello', null, {member: membersFor1[4], message: 'hello', hasAttachment: false}],
|
||||
['1', '! hello', null, {member: membersFor1[5], message: 'hello', hasAttachment: false}],
|
||||
['2', 'hello', null, undefined],
|
||||
['2', '--hello', null, undefined],
|
||||
['2', 'hello', attachmentUrl, undefined],
|
||||
|
||||
Reference in New Issue
Block a user