converted import syntax to commonJS

removed unused methods
This commit is contained in:
Aster Fialla
2026-02-16 14:29:53 -05:00
parent 055ecdf20d
commit 5ab0d62bdb
8 changed files with 764 additions and 782 deletions

View File

@@ -1,9 +1,9 @@
import { Client, Events, GatewayOpcodes } from '@fluxerjs/core'; const {messageHelper} = require('./helpers/messageHelper.js');
import { messageHelper } from "./helpers/messageHelper.js"; const {enums} = require('enums.js');
import {enums} from "./enums.js"; const {commands} = require('commands.js');
import {commands} from "./commands.js"; const {webhookHelper} = require('helpers/webhookHelper.js');
import {webhookHelper} from "./helpers/webhookHelper.js"; const {Client, Events } = require('@fluxerjs/core');
import * as env from 'dotenv'; const {env} = require('dotenv');
env.config(); env.config();
@@ -26,7 +26,7 @@ client.on(Events.MessageCreate, async (message) => {
// If message doesn't start with the bot prefix, it could still be a message with a proxy tag. If it's not, return. // If message doesn't start with the bot prefix, it could still be a message with a proxy tag. If it's not, return.
if (!content.startsWith(messageHelper.prefix)) { if (!content.startsWith(messageHelper.prefix)) {
await webhookHelper.sendMessageAsMember(client, message, content).catch((e) => { await webhookHelper.sendMessageAsMember(client, message).catch((e) => {
throw e throw e
}); });
return; return;

View File

@@ -1,12 +1,13 @@
import {messageHelper} from "./helpers/messageHelper.js"; const {messageHelper} = require('helpers/messageHelper.js')
import {enums} from "./enums.js"; const {enums} = require('enums.js')
import {memberHelper} from "./helpers/memberHelper.js"; const {memberHelper} = require('helpers/memberHelper.js')
import {EmbedBuilder} from "@fluxerjs/core"; const {importHelper} = require('helpers/importHelper.js');
import {importHelper} from "./import.js"; const {EmbedBuilder} = require('@fluxerjs/core');
const cmds = new Map();
cmds.set('member', { let commands = new Map();
commands.set('member', {
description: enums.help.SHORT_DESC_MEMBER, description: enums.help.SHORT_DESC_MEMBER,
async execute(message, client, args) { async execute(message, client, args) {
const authorFull = `${message.author.username}#${message.author.discriminator}` const authorFull = `${message.author.username}#${message.author.discriminator}`
@@ -23,10 +24,10 @@ cmds.set('member', {
} }
}) })
cmds.set('help', { commands.set('help', {
description: enums.help.SHORT_DESC_HELP, description: enums.help.SHORT_DESC_HELP,
async execute(message) { async execute(message) {
const fields = [...cmds.entries()].map(([name, cmd]) => ({ const fields = [...commands.entries()].map(([name, cmd]) => ({
name: `${messageHelper.prefix}${name}`, name: `${messageHelper.prefix}${name}`,
value: cmd.description, value: cmd.description,
inline: true, inline: true,
@@ -43,7 +44,7 @@ cmds.set('help', {
}, },
}) })
cmds.set('import', { commands.set('import', {
description: enums.help.SHORT_DESC_IMPORT, description: enums.help.SHORT_DESC_IMPORT,
async execute(message) { async execute(message) {
if (message.content.includes('--help')) { if (message.content.includes('--help')) {
@@ -70,4 +71,4 @@ cmds.set('import', {
} }
}) })
export const commands = cmds; module.exports = commands;

124
src/db.js
View File

@@ -1,5 +1,5 @@
import {DataTypes, Sequelize} from 'sequelize'; const {DataTypes, sequelize, Sequelize} = require('sequelize');
import * as env from 'dotenv'; const {env} = require('dotenv');
env.config(); env.config();
@@ -10,75 +10,75 @@ if (!password) {
process.exit(1); process.exit(1);
} }
const database = {}; const database = {
const sequelize = new Sequelize('postgres', 'postgres', password, { sequelize: new Sequelize('postgres', 'postgres', password, {
host: 'localhost', host: 'localhost',
logging: false, logging: false,
dialect: 'postgres' dialect: 'postgres'
}); }),
database.sequelize = sequelize; Sequelize: Sequelize,
database.Sequelize = Sequelize;
database.members = sequelize.define('Member', { members: sequelize.define('Member', {
userid: { userid: {
type: DataTypes.STRING, type: DataTypes.STRING,
allowNull: false, allowNull: false,
}, },
name: { name: {
type: DataTypes.STRING, type: DataTypes.STRING,
allowNull: false, allowNull: false,
}, },
displayname: { displayname: {
type: DataTypes.STRING, type: DataTypes.STRING,
}, },
propic: { propic: {
type: DataTypes.STRING, type: DataTypes.STRING,
}, },
proxy: { proxy: {
type: DataTypes.STRING, type: DataTypes.STRING,
} }
}); }),
database.systems = sequelize.define('System', { systems: sequelize.define('System', {
userid: { userid: {
type: DataTypes.STRING, type: DataTypes.STRING,
}, },
fronter: { fronter: {
type: DataTypes.STRING type: DataTypes.STRING
}, },
grouptag: { grouptag: {
type: DataTypes.STRING type: DataTypes.STRING
}, },
autoproxy: { autoproxy: {
type: DataTypes.BOOLEAN, type: DataTypes.BOOLEAN,
} }
}) }),
/** /**
* Checks Sequelize database connection. * Checks Sequelize database connection.
*/ */
database.check_connection = async function() { check_connection: async function () {
await sequelize.authenticate().then(async (result) => { await sequelize.authenticate().then(async () => {
console.log('Connection has been established successfully.'); console.log('Connection has been established successfully.');
await syncModels(); await this.syncModels();
}).catch(err => { }).catch(err => {
console.error('Unable to connect to the database:', err); console.error('Unable to connect to the database:', err);
process.exit(1); process.exit(1);
}); });
} },
/** /**
* Syncs Sequelize models. * Syncs Sequelize models.
*/ */
async function syncModels() { async syncModels() {
await sequelize.sync().then(() => { await this.sequelize.sync().then(() => {
console.log('Models synced successfully.'); console.log('Models synced successfully.');
}).catch((err) => { }).catch((err) => {
console.error('Syncing models did not work', err); console.error('Syncing models did not work', err);
process.exit(1); process.exit(1);
}); });
} }
};
export const db = database; module.exports = database;

View File

@@ -1,46 +1,46 @@
const helperEnums = {}; const enums = {
err: {
NO_MEMBER: "No such member was found.",
NO_NAME_PROVIDED: "No member name was provided for",
NO_VALUE: "has not been set for this member. Please provide a value.",
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.",
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.",
PROPIC_CANNOT_LOAD: "Profile picture could not be loaded from URL.",
NO_WEBHOOKS_ALLOWED: "Channel does not support webhooks.",
NOT_IN_SERVER: "You can only proxy in a server.",
NO_MESSAGE_SENT_WITH_PROXY: 'Proxied message has no content.',
NO_TEXT_FOR_PROXY: "You need the word 'text' for the bot to detect proxy tags with.\nCorrect usage examples: `pf;member jane proxy J:text`, `pf;member jane [text]`",
NO_PROXY_WRAPPER: "You need at least one proxy tag surrounding 'text', either before or after.\nCorrect usage examples: `pf;member jane proxy J:text`, `pf;member jane [text]`",
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.",
},
helperEnums.err = { help: {
NO_MEMBER: "No such member was found.", SHORT_DESC_HELP: "Lists available commands.",
NO_NAME_PROVIDED: "No member name was provided for", SHORT_DESC_MEMBER: "Accesses subcommands related to proxy members.",
NO_VALUE: "has not been set for this member. Please provide a value.", SHORT_DESC_IMPORT: "Imports from PluralKit.",
ADD_ERROR: "Error adding member.", 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.",
MEMBER_EXISTS: "A member with that name already exists. Please pick a unique name.", 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.",
USER_NO_MEMBERS: "You have no members created.", 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.",
DISPLAY_NAME_TOO_LONG: "The display name is too long. Please limit it to 32 characters or less.", 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.",
PROXY_EXISTS: "A duplicate proxy already exists for one of your members. Please pick a new one, or change the old one first.", REMOVE: "Removes a member based on their name, for example: `pf;member remove jane`.",
NO_SUCH_COMMAND: "No such command exists.", LIST: "Lists members in the system. Currently only lists the first 25.",
PROPIC_FAILS_REQUIREMENTS: "Profile picture must be in JPG, PNG, or WEBP format and less than 10MB.", 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.",
PROPIC_CANNOT_LOAD: "Profile picture could not be loaded from URL.", 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__.",
NO_WEBHOOKS_ALLOWED: "Channel does not support webhooks.", 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.",
NOT_IN_SERVER: "You can only proxy in a server.", 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.",
NO_MESSAGE_SENT_WITH_PROXY: 'Proxied message has no content.', 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?)."
NO_TEXT_FOR_PROXY: "You need the word 'text' for the bot to detect proxy tags with.\nCorrect usage examples: `pf;member jane proxy J:text`, `pf;member jane [text]`", },
NO_PROXY_WRAPPER: "You need at least one proxy tag surrounding 'text', either before or after.\nCorrect usage examples: `pf;member jane proxy J:text`, `pf;member jane [text]`",
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.",
}
helperEnums.help = { misc: {
SHORT_DESC_HELP: "Lists available commands.", ATTACHMENT_SENT_BY: "Attachment sent by:"
SHORT_DESC_MEMBER: "Accesses subcommands related to proxy members.", }
SHORT_DESC_IMPORT: "Imports from PluralKit.", };
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.",
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.",
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 (the stuff above), not anything else like birthdays or system handles (yet?)."
}
helperEnums.misc = { module.exports = enums;
ATTACHMENT_SENT_BY: "Attachment sent by:"
}
export const enums = helperEnums;

View File

@@ -1,23 +1,22 @@
import {enums} from "./enums.js"; const {enums} = require("../enums.js");
import {memberHelper} from "./helpers/memberHelper.js"; const {memberHelper} = require("memberHelper.js");
import {messageHelper} from "./helpers/messageHelper.js";
const ih = {}; const importHelper = {
/** /**
* Tries to import from Pluralkit. * Tries to import from Pluralkit.
* *
* @async * @async
* @param {string} authorId - The author of the message * @param {string} authorId - The author of the message
* @param {string} attachmentUrl - The attached JSON url. * @param {string} attachmentUrl - The attached JSON url.
* @returns {string} A successful addition of all members. * @returns {string} A successful addition of all members.
* @throws {Error} When the member exists, or creating a member doesn't work. * @throws {Error} When the member exists, or creating a member doesn't work.
*/ */
ih.pluralKitImport = async function (authorId, attachmentUrl) { async pluralKitImport(authorId, attachmentUrl) {
if (!attachmentUrl) { if (!attachmentUrl) {
throw new Error(enums.err.NOT_JSON_FILE); throw new Error(enums.err.NOT_JSON_FILE);
} }
return fetch(attachmentUrl).then((res) => res.json()).then(async(pkData) => { return fetch(attachmentUrl).then((res) => res.json()).then(async (pkData) => {
const pkMembers = pkData.members; const pkMembers = pkData.members;
const errors = []; const errors = [];
const addedMembers = []; const addedMembers = [];
@@ -29,7 +28,8 @@ ih.pluralKitImport = async function (authorId, attachmentUrl) {
errors.push(`${pkMember.name}: ${e.message}`); errors.push(`${pkMember.name}: ${e.message}`);
}); });
await memberHelper.checkImageFormatValidity(pkMember.avatar_url).catch(e => { await memberHelper.checkImageFormatValidity(pkMember.avatar_url).catch(e => {
errors.push(`${pkMember.name}: ${e.message}`)}); errors.push(`${pkMember.name}: ${e.message}`)
});
} }
const aggregatedText = addedMembers.length > 0 ? `Successfully added members: ${addedMembers.join(', ')}` : enums.err.NO_MEMBERS_IMPORTED; const aggregatedText = addedMembers.length > 0 ? `Successfully added members: ${addedMembers.join(', ')}` : enums.err.NO_MEMBERS_IMPORTED;
if (errors.length > 0) { if (errors.length > 0) {
@@ -37,6 +37,7 @@ ih.pluralKitImport = async function (authorId, attachmentUrl) {
} }
return aggregatedText; return aggregatedText;
}); });
} }
};
export const importHelper = ih; module.exports = importHelper;

View File

@@ -1,466 +1,456 @@
import {db} from '../db.js'; const { db } = require('../db.js')
import {enums} from "../enums.js"; const { enums} = require('../enums.js');
import {EmptyResultError, Op} from "sequelize"; const {EmptyResultError, Op} = require('sequelize');
import {EmbedBuilder} from "@fluxerjs/core"; const {EmbedBuilder} = require('@fluxerjs/core');
const mh = {}; const memberHelper = {
// Has an empty "command" to parse the help message properly
const commandList = ['--help', 'new', 'remove', 'name', 'list', 'displayName', 'proxy', 'propic', ''];
/** // Has an empty "command" to parse the help message properly
* Parses through the subcommands that come after "pf;member" and calls functions accordingly. commandList: ['--help', 'new', 'remove', 'name', 'list', 'displayName', 'proxy', 'propic', ''],
*
* @async /**
* @param {string} authorId - The id of the message author * Parses through the subcommands that come after "pf;member" and calls functions accordingly.
* @param {string} authorFull - The username and discriminator of the message author *
* @param {string[]} args - The message arguments * @async
* @param {string | null} attachmentUrl - The message attachment url. * @param {string} authorId - The id of the message author
* @param {string | null} attachmentExpiration - The message attachment expiration (if uploaded via Fluxer) * @param {string} authorFull - The username and discriminator of the message author
* @returns {Promise<string> | Promise <EmbedBuilder>} A message, or an informational embed. * @param {string[]} args - The message arguments
* @throws {Error} * @param {string | null} attachmentUrl - The message attachment url.
*/ * @param {string | null} attachmentExpiration - The message attachment expiration (if uploaded via Fluxer)
mh.parseMemberCommand = async function(authorId, authorFull, args, attachmentUrl = null, attachmentExpiration = null){ * @returns {Promise<string> | Promise <EmbedBuilder>} A message, or an informational embed.
let member; * @throws {Error}
// checks whether command is in list, otherwise assumes it's a name */
if(!commandList.includes(args[0])) { async parseMemberCommand (authorId, authorFull, args, attachmentUrl = null, attachmentExpiration = null){
member = await mh.getMemberInfo(authorId, args[0]); let member;
} // checks whether command is in list, otherwise assumes it's a name
switch(args[0]) { if(!this.commandList.includes(args[0])) {
case '--help': member = await this.getMemberInfo(authorId, args[0]);
return enums.help.MEMBER; }
case 'new': switch(args[0]) {
return await mh.addNewMember(authorId, args).catch((e) =>{throw e}); case '--help':
case 'remove': return enums.help.MEMBER;
return await mh.removeMember(authorId, args).catch((e) =>{throw e}); case 'new':
case 'name': return await this.addNewMember(authorId, args).catch((e) =>{throw e});
return enums.help.NAME; case 'remove':
case 'displayname': return await this.removeMember(authorId, args).catch((e) =>{throw e});
case 'name':
return enums.help.NAME;
case 'displayname':
return enums.help.DISPLAY_NAME;
case 'proxy':
return enums.help.PROXY;
case 'propic':
return enums.help.PROPIC;
case 'list':
if (args[1] && args[1] === "--help") {
return enums.help.LIST;
}
return await this.getAllMembersInfo(authorId, authorFull).catch((e) =>{throw e});
case '':
return enums.help.MEMBER;
}
switch(args[1]) {
case 'name':
return await this.updateName(authorId, args).catch((e) =>{throw e});
case 'displayname':
return await this.updateDisplayName(authorId, args).catch((e) =>{throw e});
case 'proxy':
if (!args[2]) return await this.getProxyByMember(authorId, args[0]).catch((e) => {throw e});
return await this.updateProxy(authorId, args).catch((e) =>{throw e});
case 'propic':
return await this.updatePropic(authorId, args, attachmentUrl, attachmentExpiration).catch((e) =>{throw e});
default:
return member;
}
},
/**
* Adds a member.
*
* @async
* @param {string} authorId - The author of the message
* @param {string[]} args - The message arguments
* @returns {Promise<string>} A successful addition.
* @throws {Error} When the member exists, or creating a member doesn't work.
*/
async addNewMember (authorId, args) {
if (args[1] && args[1] === "--help" || !args[1]) {
return enums.help.NEW;
}
const memberName = args[1];
const displayName = args[2];
return await this.addFullMember(authorId, memberName, displayName).then((member) => {
let success = `Member was successfully added.\nName: ${member.dataValues.name}`
success += displayName ? `\nDisplay name: ${member.dataValues.displayname}` : "";
return success;
}).catch(e => {
throw e;
})
},
/**
* Updates the name for a member.
*
* @async
* @param {string} authorId - The author of the message
* @param {string[]} args - The message arguments
* @returns {Promise<string>} A successful update.
* @throws {RangeError} When the name doesn't exist.
*/
async updateName (authorId, args) {
if (args[1] && args[1] === "--help" || !args[1]) {
return enums.help.DISPLAY_NAME; return enums.help.DISPLAY_NAME;
case 'proxy':
return enums.help.PROXY;
case 'propic':
return enums.help.PROPIC;
case 'list':
if (args[1] && args[1] === "--help") {
return enums.help.LIST;
}
return await mh.getAllMembersInfo(authorId, authorFull).catch((e) =>{throw e});
case '':
return enums.help.MEMBER;
}
switch(args[1]) {
case 'name':
return await mh.updateName(authorId, args).catch((e) =>{throw e});
case 'displayname':
return await mh.updateDisplayName(authorId, args).catch((e) =>{throw e});
case 'proxy':
if (!args[2]) return await mh.getProxyByMember(authorId, args[0]).catch((e) => {throw e});
return await mh.updateProxy(authorId, args).catch((e) =>{throw e});
case 'propic':
return await mh.updatePropic(authorId, args, attachmentUrl, attachmentExpiration).catch((e) =>{throw e});
default:
return member;
}
}
/**
* Adds a member.
*
* @async
* @param {string} authorId - The author of the message
* @param {string[]} args - The message arguments
* @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) {
if (args[1] && args[1] === "--help" || !args[1]) {
return enums.help.NEW;
}
const memberName = args[1];
const displayName = args[2];
return await mh.addFullMember(authorId, memberName, displayName).then((member) => {
let success = `Member was successfully added.\nName: ${member.dataValues.name}`
success += displayName ? `\nDisplay name: ${member.dataValues.displayname}` : "";
return success;
}).catch(e => {
throw e;
})
}
/**
* Updates the name for a member.
*
* @async
* @param {string} authorId - The author of the message
* @param {string[]} args - The message arguments
* @returns {Promise<string>} A successful update.
* @throws {RangeError} When the name doesn't exist.
*/
mh.updateName = async function (authorId, args) {
if (args[1] && args[1] === "--help" || !args[1]) {
return enums.help.DISPLAY_NAME;
}
const name = args[2];
const trimmedName = name ? name.trim() : null;
if (!name || trimmedName === null) {
throw new RangeError(`Display name ${enums.err.NO_VALUE}`);
}
return await mh.updateMemberField(authorId, args).catch((e) =>{throw e});
}
/**
* Updates the display name for a member.
*
* @async
* @param {string} authorId - The author of the message
* @param {string[]} args - The message arguments
* @returns {Promise<string>} A successful update.
* @throws {RangeError} When the display name is too long or doesn't exist.
*/
mh.updateDisplayName = async function(authorId, args) {
if (args[1] && args[1] === "--help" || !args[1]) {
return enums.help.DISPLAY_NAME;
}
const memberName = args[0];
const displayName = args[2];
const trimmedName = displayName ? displayName.trim() : null;
if (!displayName || trimmedName === null ) {
return await mh.getMemberByName(authorId, memberName).then((member) => {
if (member && member.displayname) {
return `Display name for ${memberName} is: \"${member.displayname}\".`;
}
else if (member) {
throw new RangeError(`Display name ${enums.err.NO_VALUE}`);
}
});
}
else if (displayName.length > 32) {
throw new RangeError(enums.err.DISPLAY_NAME_TOO_LONG);
}
return await mh.updateMemberField(authorId, args).catch((e) =>{throw e});
}
/**
* Updates the proxy for a member, first checking that no other members attached to the author have the tag.
*
* @async
* @param {string} authorId - The author of the message
* @param {string[]} args - The message arguments
* @returns {Promise<string> } A successful update.
* @throws {RangeError | Error} When an empty proxy was provided, or no proxy exists.
*/
mh.updateProxy = async function(authorId, args) {
if (args[2] && args[2] === "--help") {
return enums.help.PROXY;
}
const proxyExists = await mh.checkIfProxyExists(authorId, args[2]).then((proxyExists) => {
return proxyExists;
}).catch((e) =>{throw e});
if (!proxyExists) {
return await mh.updateMemberField(authorId, args).catch((e) =>{throw e});
}
}
/**
* Updates the profile pic for a member, based on either the attachment or the args provided.
*
* @async
* @param {string} authorId - The author of the message
* @param {string[]} args - The message arguments
* @param {string} attachmentUrl - The url of the first attachment in the message
* @param {string | null} attachmentExpiry - The expiration date of the first attachment in the message (if uploaded to Fluxer)
* @returns {Promise<string>} A successful update.
* @throws {Error} When loading the profile picture from a URL doesn't work.
*/
mh.updatePropic = async function(authorId, args, attachmentUrl, attachmentExpiry= null) {
if (args[1] && args[1] === "--help") {
return enums.help.PROPIC;
}
let img;
const updatedArgs = args;
if (!updatedArgs[1] && !attachmentUrl) {
return enums.help.PROPIC;
} else if (attachmentUrl) {
updatedArgs[2] = attachmentUrl;
updatedArgs[3] = attachmentExpiry;
}
if (updatedArgs[2]) {
img = updatedArgs[2];
}
const isValidImage = await mh.checkImageFormatValidity(img).catch((e) =>{throw e});
if (isValidImage) {
return await mh.updateMemberField(authorId, updatedArgs).catch((e) =>{throw e});
}
}
/**
* Checks if an uploaded picture is in the right format.
*
* @async
* @param {string} imageUrl - The url of the image
* @returns {Promise<boolean>} - If the image is a valid format.
* @throws {Error} When loading the profile picture from a URL doesn't work, or it fails requirements.
*/
mh.checkImageFormatValidity = async function(imageUrl) {
const acceptableImages = ['image/png', 'image/jpg', 'image/jpeg', 'image/webp'];
return await fetch(imageUrl).then(r => r.blob()).then(blobFile => {
if (blobFile.size > 1000000 || !acceptableImages.includes(blobFile.type)) throw new Error(enums.err.PROPIC_FAILS_REQUIREMENTS);
return true;
}).catch((error) => {
throw new Error(`${enums.err.PROPIC_CANNOT_LOAD}: ${error.message}`);
});
}
/**
* Removes a member.
*
* @async
* @param {string} authorId - The author of the message
* @param {string[]} args - The message arguments
* @returns {Promise<string>} A successful removal.
* @throws {EmptyResultError} When there is no member to remove.
*/
mh.removeMember = async function(authorId, args) {
if (args[1] && args[1] === "--help" || !args[1]) {
return enums.help.REMOVE;
}
const memberName = args[1];
return await db.members.destroy({ where: { name: {[Op.iLike]: memberName}, userid: authorId } }).then((result) => {
if (result) {
return `Member "${memberName}" has been deleted.`;
} }
throw new EmptyResultError(`${enums.err.NO_MEMBER}`);
})
}
/*======Non-Subcommands======*/ const name = args[2];
const trimmedName = name ? name.trim() : null;
/** if (!name || trimmedName === null) {
* Adds a member with full details, first checking that there is no member of that name associated with the author. throw new RangeError(`Display name ${enums.err.NO_VALUE}`);
*
* @async
* @param {string} authorId - The author of the message
* @param {string} memberName - The name of the member.
* @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.
*/
mh.addFullMember = async function(authorId, memberName, displayName = null, proxy = null, propic= null, isImport = false) {
await mh.getMemberByName(authorId, memberName).then((member) => {
if (member) {
throw new Error(`Can't add ${memberName}. ${enums.err.MEMBER_EXISTS}`);
} }
}); return await this.updateMemberField(authorId, args).catch((e) =>{throw e});
if (displayName) { },
/**
* Updates the display name for a member.
*
* @async
* @param {string} authorId - The author of the message
* @param {string[]} args - The message arguments
* @returns {Promise<string>} A successful update.
* @throws {RangeError} When the display name is too long or doesn't exist.
*/
async updateDisplayName (authorId, args) {
if (args[1] && args[1] === "--help" || !args[1]) {
return enums.help.DISPLAY_NAME;
}
const memberName = args[0];
const displayName = args[2];
const trimmedName = displayName ? displayName.trim() : null; const trimmedName = displayName ? displayName.trim() : null;
if (trimmedName && trimmedName.length > 32) {
throw new RangeError(`Can't add ${memberName}. ${enums.err.DISPLAY_NAME_TOO_LONG}`); if (!displayName || trimmedName === null ) {
return await this.getMemberByName(authorId, memberName).then((member) => {
if (member && member.displayname) {
return `Display name for ${memberName} is: \"${member.displayname}\".`;
}
else if (member) {
throw new RangeError(`Display name ${enums.err.NO_VALUE}`);
}
});
} }
} else if (displayName.length > 32) {
if (proxy) { throw new RangeError(enums.err.DISPLAY_NAME_TOO_LONG);
await mh.checkIfProxyExists(authorId, proxy).catch((e) =>{throw e}); }
} return await this.updateMemberField(authorId, args).catch((e) =>{throw e});
let validPropic; },
if (propic) {
validPropic = await mh.checkImageFormatValidity(propic).then((valid) => { /**
return valid; * Updates the proxy for a member, first checking that no other members attached to the author have the tag.
}).catch((e) =>{ *
if (!isImport) { * @async
throw (e); * @param {string} authorId - The author of the message
} * @param {string[]} args - The message arguments
return false; * @returns {Promise<string> } A successful update.
* @throws {RangeError | Error} When an empty proxy was provided, or no proxy exists.
*/
async updateProxy (authorId, args) {
if (args[2] && args[2] === "--help") {
return enums.help.PROXY;
}
const proxyExists = await this.checkIfProxyExists(authorId, args[2]).then((proxyExists) => {
return proxyExists;
}).catch((e) =>{throw e});
if (!proxyExists) {
return await this.updateMemberField(authorId, args).catch((e) =>{throw e});
}
},
/**
* Updates the profile pic for a member, based on either the attachment or the args provided.
*
* @async
* @param {string} authorId - The author of the message
* @param {string[]} args - The message arguments
* @param {string} attachmentUrl - The url of the first attachment in the message
* @param {string | null} attachmentExpiry - The expiration date of the first attachment in the message (if uploaded to Fluxer)
* @returns {Promise<string>} A successful update.
* @throws {Error} When loading the profile picture from a URL doesn't work.
*/
async updatePropic (authorId, args, attachmentUrl, attachmentExpiry= null) {
if (args[1] && args[1] === "--help") {
return enums.help.PROPIC;
}
let img;
const updatedArgs = args;
if (!updatedArgs[1] && !attachmentUrl) {
return enums.help.PROPIC;
} else if (attachmentUrl) {
updatedArgs[2] = attachmentUrl;
updatedArgs[3] = attachmentExpiry;
}
if (updatedArgs[2]) {
img = updatedArgs[2];
}
const isValidImage = await this.checkImageFormatValidity(img).catch((e) =>{throw e});
if (isValidImage) {
return await this.updateMemberField(authorId, updatedArgs).catch((e) =>{throw e});
}
},
/**
* Checks if an uploaded picture is in the right format.
*
* @async
* @param {string} imageUrl - The url of the image
* @returns {Promise<boolean>} - If the image is a valid format.
* @throws {Error} When loading the profile picture from a URL doesn't work, or it fails requirements.
*/
async checkImageFormatValidity (imageUrl) {
const acceptableImages = ['image/png', 'image/jpg', 'image/jpeg', 'image/webp'];
return await fetch(imageUrl).then(r => r.blob()).then(blobFile => {
if (blobFile.size > 1000000 || !acceptableImages.includes(blobFile.type)) throw new Error(enums.err.PROPIC_FAILS_REQUIREMENTS);
return true;
}).catch((error) => {
throw new Error(`${enums.err.PROPIC_CANNOT_LOAD}: ${error.message}`);
}); });
} },
const member = await db.members.create({ /**
name: memberName, * Removes a member.
userid: authorId, *
displayname: displayName, * @async
proxy: proxy, * @param {string} authorId - The author of the message
propic: validPropic ? propic : null, * @param {string[]} args - The message arguments
}); * @returns {Promise<string>} A successful removal.
if (!member) { * @throws {EmptyResultError} When there is no member to remove.
new Error(`${enums.err.ADD_ERROR}: ${e.message}`); */
} async removeMember (authorId, args) {
} if (args[1] && args[1] === "--help" || !args[1]) {
return enums.help.REMOVE;
/**
* Updates one fields for a member in the database.
*
* @async
* @param {string} authorId - The author of the message
* @param {string[]} args - The message arguments
* @returns {Promise<string>} A successful update.
* @throws {EmptyResultError | Error} When the member is not found, or catchall error.
*/
mh.updateMemberField = async function(authorId, args) {
const memberName = args[0];
const columnName = args[1];
const value = args[2];
let fluxerPropicWarning;
// indicates that an attachment was uploaded on Fluxer directly
if (columnName === "propic" && args[3]) {
fluxerPropicWarning = mh.setExpirationWarning(args[3]);
}
return await db.members.update({[columnName]: value}, { where: { name: {[Op.iLike]: memberName}, userid: authorId } }).then(() => {
return `Updated ${columnName} for ${memberName} to ${value}${fluxerPropicWarning ?? ''}.`;
}).catch(e => {
if (e === EmptyResultError) {
throw new EmptyResultError(`Can't update ${memberName}. ${enums.err.NO_MEMBER}: ${e.message}`);
} }
else {
throw new Error(`Can't update ${memberName}. ${e.message}`);
}
});
}
/** const memberName = args[1];
* Sets the warning for an expiration date. return await db.members.destroy({ where: { name: {[Op.iLike]: memberName}, userid: authorId } }).then((result) => {
* if (result) {
* @param {string} expirationString - An expiration date string. return `Member "${memberName}" has been deleted.`;
* @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`
}
}
/**
* Gets the details for a member.
*
* @async
* @param {string} authorId - The author of the message
* @param {string} memberName - The message arguments
* @returns {Promise<EmbedBuilder>} The member's info.
*/
mh.getMemberInfo = async function (authorId, memberName) {
return await mh.getMemberByName(authorId, memberName).then((member) => {
if (member) {
return new EmbedBuilder()
.setTitle(member.name)
.setDescription(`Details for ${member.name}`)
.addFields(
{name: 'Display name: ', value: member.displayname ?? 'unset', inline: true},
{name: 'Proxy tag: ', value: member.proxy ?? 'unset', inline: true},
)
.setImage(member.propic);
}
});
}
/**
* Gets all members for an author.
*
* @async
* @param {string} authorId - The id of the message author
* @param {string} authorName - The id name the message author
* @returns {Promise<EmbedBuilder>} The info for all members.
* @throws {Error} When there are no members for an author.
*/
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]) => ({
name: member.name,
value: `(Proxy: \`${member.proxy ?? "unset"}\`)`,
inline: true,
}));
return new EmbedBuilder()
.setTitle(`${fields > 25 ? "First 25 m" : "M"}embers for ${authorName}`)
.addFields(...fields);
}
/**
* Gets a member based on the author and proxy tag.
*
* @async
* @param {string} authorId - The author of the message.
* @param {string} memberName - The member's name.
* @returns {Promise<model>} The member object.
* @throws { EmptyResultError } When the member is not found.
*/
mh.getMemberByName = async function(authorId, memberName) {
return await db.members.findOne({ where: { userid: authorId, name: {[Op.iLike]: memberName}}});
}
/**
* Gets a member based on the author and proxy tag.
*
* @async
* @param {string} authorId - The author of the message.
* @param {string} memberName - The member's name.
* @returns {Promise<string>} The member object.
* @throws { EmptyResultError } When the member is not found.
*/
mh.getProxyByMember = async function(authorId, memberName) {
return await mh.getMemberByName(authorId, memberName).then((member) => {
if (member) {
return member.dataValues.proxy;
}
throw new EmptyResultError(enums.err.NO_MEMBER);
})
}
/**
* Gets a member based on the author and proxy tag.
*
* @async
* @param {string} authorId - The author of the message
* @param {string} proxy - The proxy tag
* @returns {Promise<model>} The member object.
*/
mh.getMemberByProxy = async function(authorId, proxy) {
return await db.members.findOne({ where: { userid: authorId, proxy: proxy } });
}
/**
* Gets all members belonging to the author.
*
* @async
* @param {string} authorId - The author of the message
* @returns {Promise<model[] | null>} The member object array.
*/
mh.getMembersByAuthor = async function(authorId) {
return await db.members.findAll({ where: { userid: authorId } });
}
/**
* Checks if proxy exists for a member.
*
* @param {string} authorId - The author of the message
* @param {string} proxy - The proxy tag.
* @returns {Promise<boolean> } Whether the proxy exists.
* @throws {Error} When an empty proxy was provided, or no proxy exists.
*/
mh.checkIfProxyExists = async function(authorId, proxy) {
if (proxy) {
const splitProxy = proxy.trim().split("text");
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}); throw new EmptyResultError(`${enums.err.NO_MEMBER}`);
} })
},
/*======Non-Subcommands======*/
/**
* Adds a member with full details, first checking that there is no member of that name associated with the author.
*
* @async
* @param {string} authorId - The author of the message
* @param {string} memberName - The name of the member.
* @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 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.
*/
async addFullMember (authorId, memberName, displayName = null, proxy = null, propic= null, isImport = false) {
await this.getMemberByName(authorId, memberName).then((member) => {
if (member) {
throw new Error(`Can't add ${memberName}. ${enums.err.MEMBER_EXISTS}`);
}
});
if (displayName) {
const trimmedName = displayName ? displayName.trim() : null;
if (trimmedName && trimmedName.length > 32) {
throw new RangeError(`Can't add ${memberName}. ${enums.err.DISPLAY_NAME_TOO_LONG}`);
}
}
if (proxy) {
await this.checkIfProxyExists(authorId, proxy).catch((e) =>{throw e});
}
let validPropic;
if (propic) {
validPropic = await this.checkImageFormatValidity(propic).then((valid) => {
return valid;
}).catch((e) =>{
if (!isImport) {
throw (e);
}
return false;
});
}
const member = await db.members.create({
name: memberName,
userid: authorId,
displayname: displayName,
proxy: proxy,
propic: validPropic ? propic : null,
});
if (!member) {
new Error(`${enums.err.ADD_ERROR}`);
}
},
/**
* Updates one fields for a member in the database.
*
* @async
* @param {string} authorId - The author of the message
* @param {string[]} args - The message arguments
* @returns {Promise<string>} A successful update.
* @throws {EmptyResultError | Error} When the member is not found, or catchall error.
*/
async updateMemberField (authorId, args) {
const memberName = args[0];
const columnName = args[1];
const value = args[2];
let fluxerPropicWarning;
// indicates that an attachment was uploaded on Fluxer directly
if (columnName === "propic" && args[3]) {
fluxerPropicWarning = this.setExpirationWarning(args[3]);
}
return await db.members.update({[columnName]: value}, { where: { name: {[Op.iLike]: memberName}, userid: authorId } }).then(() => {
return `Updated ${columnName} for ${memberName} to ${value}${fluxerPropicWarning ?? ''}.`;
}).catch(e => {
if (e === EmptyResultError) {
throw new EmptyResultError(`Can't update ${memberName}. ${enums.err.NO_MEMBER}: ${e.message}`);
}
else {
throw new Error(`Can't update ${memberName}. ${e.message}`);
}
});
},
/**
* 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.
*/
setExpirationWarning(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`
}
},
/**
* Gets the details for a member.
*
* @async
* @param {string} authorId - The author of the message
* @param {string} memberName - The message arguments
* @returns {Promise<EmbedBuilder>} The member's info.
*/
async getMemberInfo (authorId, memberName) {
return await this.getMemberByName(authorId, memberName).then((member) => {
if (member) {
return new EmbedBuilder()
.setTitle(member.name)
.setDescription(`Details for ${member.name}`)
.addFields(
{name: 'Display name: ', value: member.displayname ?? 'unset', inline: true},
{name: 'Proxy tag: ', value: member.proxy ?? 'unset', inline: true},
)
.setImage(member.propic);
}
});
},
/**
* Gets all members for an author.
*
* @async
* @param {string} authorId - The id of the message author
* @param {string} authorName - The id name the message author
* @returns {Promise<EmbedBuilder>} The info for all members.
* @throws {Error} When there are no members for an author.
*/
async getAllMembersInfo (authorId, authorName) {
const members = await this.getMembersByAuthor(authorId);
if (members == null) throw Error(enums.err.USER_NO_MEMBERS);
const fields = [...members.entries()].map(([name, member]) => ({
name: member.name,
value: `(Proxy: \`${member.proxy ?? "unset"}\`)`,
inline: true,
}));
return new EmbedBuilder()
.setTitle(`${fields > 25 ? "First 25 m" : "M"}embers for ${authorName}`)
.addFields(...fields);
},
/**
* Gets a member based on the author and proxy tag.
*
* @async
* @param {string} authorId - The author of the message.
* @param {string} memberName - The member's name.
* @returns {Promise<model>} The member object.
* @throws { EmptyResultError } When the member is not found.
*/
async getMemberByName (authorId, memberName) {
return await db.members.findOne({ where: { userid: authorId, name: {[Op.iLike]: memberName}}});
},
/**
* Gets a member based on the author and proxy tag.
*
* @async
* @param {string} authorId - The author of the message.
* @param {string} memberName - The member's name.
* @returns {Promise<model>} The member object.
* @throws { EmptyResultError } When the member is not found.
*/
async getProxyByMember (authorId, memberName) {
return await this.getMemberByName(authorId, memberName).then((member) => {
if (member) {
return member.dataValues.proxy;
}
throw new EmptyResultError(enums.err.NO_MEMBER);
})
},
/**
* Gets all members belonging to the author.
*
* @async
* @param {string} authorId - The author of the message
* @returns {Promise<model[] | null>} The member object array.
*/
async getMembersByAuthor (authorId) {
return await db.members.findAll({ where: { userid: authorId } });
},
/**
* Checks if proxy exists for a member.
*
* @param {string} authorId - The author of the message
* @param {string} proxy - The proxy tag.
* @returns {Promise<boolean> } Whether the proxy exists.
* @throws {Error} When an empty proxy was provided, or no proxy exists.
*/
async checkIfProxyExists (authorId, proxy) {
if (proxy) {
const splitProxy = proxy.trim().split("text");
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 this.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});
}
}
} }
export const memberHelper = mh; module.exports = memberHelper;

View File

@@ -1,95 +1,96 @@
import {memberHelper} from "./memberHelper.js"; const {memberHelper} = require('memberHelper.js');
import {enums} from "../enums.js"; const {fs} = require('fs')
import tmp, {setGracefulCleanup} from "tmp"; const {tmp, setGracefulCleanup } = require('tmp')
import fs from 'fs'; const {enums} = require('../enums.js')
import {Message} from "@fluxerjs/core"; const {Message} = require('@fluxerjs/core');
const msgh = {};
msgh.prefix = "pf;"
setGracefulCleanup(); setGracefulCleanup();
/** const messageHelper = {
* Parses and slices up message arguments, retaining quoted strings.
*
* @param {string} content - The full message content.
* @param {string} commandName - The command name.
* @returns {string[]} An array of arguments.
*/
msgh.parseCommandArgs = function(content, commandName) {
const message = content.slice(msgh.prefix.length + commandName.length).trim();
return message.match(/\\?.|^$/g).reduce((accumulator, chara) => { prefix: "pf;",
if (chara === '"') {
// checks whether string is within quotes or not /**
accumulator.quote ^= 1; * Parses and slices up message arguments, retaining quoted strings.
} else if (!accumulator.quote && chara === ' '){ *
// if not currently in quoted string, push empty string to start word * @param {string} content - The full message content.
accumulator.array.push(''); * @param {string} commandName - The command name.
} else { * @returns {string[]} An array of arguments.
// accumulates characters to the last string in the array and removes escape characters */
accumulator.array[accumulator.array.length-1] += chara.replace(/\\(.)/,"$1"); parseCommandArgs(content, commandName) {
} const message = content.slice(this.prefix.length + commandName.length).trim();
return accumulator;
}, {array: ['']}).array // initial array with empty string for the reducer
}
/** return message.match(/\\?.|^$/g).reduce((accumulator, chara) => {
* Parses messages to see if any part of the text matches the tags of any member belonging to an author. if (chara === '"') {
* // checks whether string is within quotes or not
* @param {string} authorId - The author of the message. accumulator.quote ^= 1;
* @param {string} content - The full message content } else if (!accumulator.quote && chara === ' ') {
* @param {string | null} attachmentUrl - The url for an attachment to the message, if any exists. // if not currently in quoted string, push empty string to start word
* @returns {Object} The proxy message object. accumulator.array.push('');
* @throws {Error} If a proxy message is sent with no message within it. } else {
*/ // accumulates characters to the last string in the array and removes escape characters
msgh.parseProxyTags = async function (authorId, content, attachmentUrl= null){ accumulator.array[accumulator.array.length - 1] += chara.replace(/\\(.)/, "$1");
const members = await memberHelper.getMembersByAuthor(authorId);
// If an author has no members, no sense in searching for proxy
if (members.length === 0) {
return;
}
const proxyMessage = {}
members.forEach(member => {
if (member.proxy) {
const splitProxy = member.proxy.split("text");
if(content.startsWith(splitProxy[0]) && content.endsWith(splitProxy[1])) {
proxyMessage.member = member;
if (attachmentUrl) return proxyMessage.message = enums.misc.ATTACHMENT_SENT_BY;
let escapedPrefix = splitProxy[0].replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
let escapedSuffix = splitProxy[1].replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
escapedPrefix = new RegExp("^" + escapedPrefix);
escapedSuffix = new RegExp(escapedSuffix + "$")
proxyMessage.message = content.replace(escapedPrefix, "").replace(escapedSuffix, "");
if (proxyMessage.message.length === 0) throw new Error(enums.err.NO_MESSAGE_SENT_WITH_PROXY);
} }
return accumulator;
}, {array: ['']}).array // initial array with empty string for the reducer
},
/**
* Parses messages to see if any part of the text matches the tags of any member belonging to an author.
*
* @param {string} authorId - The author of the message.
* @param {string} content - The full message content
* @param {string | null} attachmentUrl - The url for an attachment to the message, if any exists.
* @returns {Object} The proxy message object.
* @throws {Error} If a proxy message is sent with no message within it.
*/
async parseProxyTags(authorId, content, attachmentUrl = null) {
const members = await memberHelper.getMembersByAuthor(authorId);
// If an author has no members, no sense in searching for proxy
if (members.length === 0) {
return;
} }
})
return proxyMessage;
}
/** const proxyMessage = {}
* Sends a message as an attachment if it's too long.NOT CURRENTLY IN USE members.forEach(member => {
* if (member.proxy) {
* @async const splitProxy = member.proxy.split("text");
* @param {string} text - The text of the message. if (content.startsWith(splitProxy[0]) && content.endsWith(splitProxy[1])) {
* @param {Message} message - The message object. proxyMessage.member = member;
* @throws {Error} If a proxy message is sent with no message within it. if (attachmentUrl) return proxyMessage.message = enums.misc.ATTACHMENT_SENT_BY;
*
*/ let escapedPrefix = splitProxy[0].replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
msgh.sendMessageAsAttachment = async function(text, message) { let escapedSuffix = splitProxy[1].replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
if (text.length > 2000) { escapedPrefix = new RegExp("^" + escapedPrefix);
tmp.file(async (err, path, fd, cleanupCallback) => { escapedSuffix = new RegExp(escapedSuffix + "$")
fs.writeFile(path, text, (err) => { proxyMessage.message = content.replace(escapedPrefix, "").replace(escapedSuffix, "");
if (proxyMessage.message.length === 0) throw new Error(enums.err.NO_MESSAGE_SENT_WITH_PROXY);
}
}
})
return proxyMessage;
},
/**
* Sends a message as an attachment if it's too long.NOT CURRENTLY IN USE
*
* @async
* @param {string} text - The text of the message.
* @param {Message} message - The message object.
* @throws {Error} If a proxy message is sent with no message within it.
*
*/
async sendMessageAsAttachment(text, message) {
if (text.length > 2000) {
tmp.file(async (err, path, fd, cleanupCallback) => {
fs.writeFile(path, text, (err) => {
if (err) throw err;
})
if (err) throw err; if (err) throw err;
}) await message.reply({content: enums.err.IMPORT_ERROR, attachments: [path]});
if (err) throw err; });
await message.reply({content: enums.err.IMPORT_ERROR, attachments: [path]}); }
});
} }
} };
export const messageHelper = msgh; module.exports = messageHelper;

View File

@@ -1,112 +1,101 @@
import {messageHelper} from "./messageHelper.js"; const {messageHelper} = require('messageHelper.js');
import {memberHelper} from "./memberHelper.js"; const {enums} = require('../enums.js');
import {Webhook, Channel, Message, EmbedBuilder} from '@fluxerjs/core'; const {Client, Message, Webhook, Channel} = require('@fluxerjs/core');
import {enums} from "../enums.js";
const wh = {};
const name = 'PluralFlux Proxy Webhook'; const name = 'PluralFlux Proxy Webhook';
/** const webhookHelper = {
* Replaces a proxied message with a webhook using the member information. /**
* @param {Client} client - The fluxer.js client. * Replaces a proxied message with a webhook using the member information.
* @param {Message} message - The full message object. * @param {Client} client - The fluxer.js client.
* @throws {Error} When the proxy message is not in a server. * @param {Message} message - The full message object.
*/ * @throws {Error} When the proxy message is not in a server.
wh.sendMessageAsMember = async function(client, message) { */
const attachmentUrl = message.attachments.size > 0 ? message.attachments.first().url : null; async sendMessageAsMember(client, message) {
const proxyMatch = await messageHelper.parseProxyTags(message.author.id, message.content, attachmentUrl).catch(e =>{throw e}); const attachmentUrl = message.attachments.size > 0 ? message.attachments.first().url : null;
// If the message doesn't match a proxy, just return. const proxyMatch = await messageHelper.parseProxyTags(message.author.id, message.content, attachmentUrl).catch(e => {
if (!proxyMatch.member) { throw e
return; });
} // If the message doesn't match a proxy, just return.
// If the message does match a proxy but is not in a guild server (ex: in the Bot's DMs if (!proxyMatch.member) {
if (!message.guildId) { return;
throw new Error(enums.err.NOT_IN_SERVER);
}
if (proxyMatch.message === enums.misc.ATTACHMENT_SENT_BY) {
return await message.reply(`${enums.misc.ATTACHMENT_SENT_BY} ${proxyMatch.member.displayname}`)
}
await replaceMessage(client, message, proxyMatch.message, proxyMatch.member).catch(e =>{throw e});
}
/**
* Replaces a proxied message with a webhook using the member information.
* @param {Client} client - The fluxer.js client.
* @param {Message} message - The message to be deleted.
* @param {string} text - The text to send via the webhook.
* @param {model} member - A member object from the database.
* @throws {Error} When there's no message to send.
*/
async function replaceMessage(client, message, text, member) {
if (text.length > 0 || message.attachments.size > 0) {
const channel = client.channels.get(message.channelId);
const webhook = await getOrCreateWebhook(client, channel).catch((e) =>{throw e});
const username = member.displayname ?? member.name;
await webhook.send({content: text, username: username, avatar_url: member.propic});
await message.delete();
}
else {
throw new Error(enums.err.NO_MESSAGE_SENT_WITH_PROXY);
}
}
/**
* Creates attachment embeds for the webhook since right now sending images is not supported.
*
* @param {Object[]} attachments - The attachments.
* @returns {Object[]} A series of embeds.
*/
function createAttachmentEmbedsForWebhook(attachments) {
let embeds = [];
attachments.forEach(attachment => {
const embed = new EmbedBuilder()
.setTitle(attachment.filename)
.setImage(attachment.url).toJSON()
embeds.push(embed);
});
return embeds;
}
/**
* Gets or creates a webhook.
*
* @param {Client} client - The fluxer.js client.
* @param {Channel} channel - The channel the message was sent in.
* @returns {Webhook} A webhook object.
* @throws {Error} When no webhooks are allowed in the channel.
*/
async function getOrCreateWebhook(client, channel) {
// If channel doesn't allow webhooks
if (!channel?.createWebhook) throw new Error(enums.err.NO_WEBHOOKS_ALLOWED);
let webhook = await getWebhook(client, channel).catch((e) =>{throw e});
if (!webhook) {
webhook = await channel.createWebhook({name: name});
}
return webhook;
}
/**
* Gets an existing webhook.
*
* @param {Client} client - The fluxer.js client.
* @param {Channel} channel - The channel the message was sent in.
* @returns {Webhook} A webhook object.
*/
async function getWebhook(client, channel) {
const channelWebhooks = await channel?.fetchWebhooks() ?? [];
if (channelWebhooks.length === 0) {
return;
}
let pf_webhook;
channelWebhooks.forEach((webhook) => {
if (webhook.name === name) {
pf_webhook = webhook;
} }
}) // If the message does match a proxy but is not in a guild server (ex: in the Bot's DMs
return pf_webhook; if (!message.guildId) {
throw new Error(enums.err.NOT_IN_SERVER);
}
if (proxyMatch.message === enums.misc.ATTACHMENT_SENT_BY) {
return await message.reply(`${enums.misc.ATTACHMENT_SENT_BY} ${proxyMatch.member.displayname}`)
}
await this.replaceMessage(client, message, proxyMatch.message, proxyMatch.member).catch(e => {
throw e
});
},
/**
* Replaces a proxied message with a webhook using the member information.
* @param {Client} client - The fluxer.js client.
* @param {Message} message - The message to be deleted.
* @param {string} text - The text to send via the webhook.
* @param {model} member - A member object from the database.
* @throws {Error} When there's no message to send.
*/
async replaceMessage(client, message, text, member) {
if (text.length > 0 || message.attachments.size > 0) {
const channel = client.channels.get(message.channelId);
const webhook = await this.getOrCreateWebhook(client, channel).catch((e) => {
throw e
});
const username = member.displayname ?? member.name;
await webhook.send({content: text, username: username, avatar_url: member.propic});
await message.delete();
} else {
throw new Error(enums.err.NO_MESSAGE_SENT_WITH_PROXY);
}
},
/**
* Gets or creates a webhook.
*
* @param {Client} client - The fluxer.js client.
* @param {Channel} channel - The channel the message was sent in.
* @returns {Webhook} A webhook object.
* @throws {Error} When no webhooks are allowed in the channel.
*/
async getOrCreateWebhook(client, channel) {
// If channel doesn't allow webhooks
if (!channel?.createWebhook) throw new Error(enums.err.NO_WEBHOOKS_ALLOWED);
let webhook = await this.getWebhook(client, channel).catch((e) => {
throw e
});
if (!webhook) {
webhook = await channel.createWebhook({name: name});
}
return webhook;
},
/**
* Gets an existing webhook.
*
* @param {Client} client - The fluxer.js client.
* @param {Channel} channel - The channel the message was sent in.
* @returns {Webhook} A webhook object.
*/
async getWebhook(client, channel) {
const channelWebhooks = await channel?.fetchWebhooks() ?? [];
if (channelWebhooks.length === 0) {
return;
}
let pf_webhook;
channelWebhooks.forEach((webhook) => {
if (webhook.name === name) {
pf_webhook = webhook;
}
})
return pf_webhook;
}
} }
export const webhookHelper = wh;
module.exports = webhookHelper;