From a4892d854b58bdf76966d2ecaa0e3936756e8c8c Mon Sep 17 00:00:00 2001 From: spiral Date: Fri, 16 Jul 2021 19:35:06 -0400 Subject: [PATCH] Update help command to use buttons & text input also, move messages / "custom commands" to JSON file --- bot.js | 3 + messages.js | 216 +++++++++++++++++++ model/command-category.js | 3 - model/command.js | 45 ++-- model/command/add-friend.js | 40 ---- model/command/add-set-front.js | 52 ----- model/command/custom-fields.js | 39 ---- model/command/custom-front.js | 42 ---- model/command/embed.js | 2 +- model/command/front-history-importing.js | 53 ----- model/command/help.js | 263 +++++++++-------------- model/command/messaging.js | 43 ---- model/command/notifications.js | 40 ---- model/command/see-members.js | 39 ---- model/command/sync-members-pluralkit.js | 73 ------- model/command/system-relationships.js | 40 ---- model/command/website.js | 40 ---- model/messages.js | 54 +++++ 18 files changed, 396 insertions(+), 691 deletions(-) create mode 100644 messages.js delete mode 100644 model/command/add-friend.js delete mode 100644 model/command/add-set-front.js delete mode 100644 model/command/custom-fields.js delete mode 100644 model/command/custom-front.js delete mode 100644 model/command/front-history-importing.js delete mode 100644 model/command/messaging.js delete mode 100644 model/command/notifications.js delete mode 100644 model/command/see-members.js delete mode 100644 model/command/sync-members-pluralkit.js delete mode 100644 model/command/system-relationships.js delete mode 100644 model/command/website.js create mode 100644 model/messages.js diff --git a/bot.js b/bot.js index 9460aab..646eaf2 100644 --- a/bot.js +++ b/bot.js @@ -74,6 +74,9 @@ const botProcess = () => { Command.init(); + const help = require("./model/command/help"); + bot.ws.on('INTERACTION_CREATE', help.interactionHandler); + bot.on('ready', () => { fs.readdirSync('./event/bot/') .filter(filename => filename.endsWith('.js')) diff --git a/messages.js b/messages.js new file mode 100644 index 0000000..9e002d4 --- /dev/null +++ b/messages.js @@ -0,0 +1,216 @@ +// interface Message { +// name string[]; +// description string; +// title string; +// text string; +// } + +module.exports = [ + { + names: [ + "add-friend", + "add-friends", + "addfriend", + "af", + ], + description: 'Explains how to troubleshoot not being able to add a friend.', + title: "Add friend", + "text": + 'If you and your friends are unable to add each other as friends, or can\'t find each other as friends, ' + + 'make sure that you are using a case sensitive username when adding each other. Additionally, it is ' + + 'currently not possible to add people by their user ID, so if you are using a user id to add them as ' + + 'friend this currently does not work but will in the future.' + }, + { + names: [ + "add-set-front", + 'addsetfront', + 'addset-front', + 'add-setfront', + 'add-set', + 'addset', + 'add-front', + 'addfront', + 'set-front', + 'setfront', + 'asf', + ], + description: 'Talks about how to set who is fronting in the application.', + title: "Add / set front", + text: + 'There are two ways in-app to show a member as fronting. Set as front, and add to front.\n' + + '\n' + + 'Using set as front will clear all members from your front list, and then add that member. Add to front ' + + 'won\'t remove anyone from the list, but it will add the member whose add icon you tapped. You can ' + + 'individually remove members by tapping the downward facing arrow next to their name.' + }, + { + names: [ + "custom-fields", + "customfields", + "customfield", + "cfi", + ], + description: 'Talks about the "custom fields" feature.', + title: "Custom fields", + text: + 'The ability to change the fields shown on your member lists is planned. This feature will also bring ' + + 'the ability to remove and add fields, as well as make fields private, public, or show them to only ' + + 'trusted friends.' + }, + { + names: [ + "custom-front", + "custom-fronts", + "customfront", + "customfronts", + "cf", + ], + description: 'Explains what "custom front" is.', + title: "Custom fronts", + text: + 'Custom fronts is a kind of status for fronts, like "blurred", "unknown member", "dissociated", etc... \n' + + '\n' + + 'You don\'t want those to show up as real members in your system list but you still want to be able to ' + + 'set front as one of them — that\'s where custom fronts kick in.\n' + + '\n' + + 'They\'re highly customizable (as per popular request) so you can name them anything you want.' + }, + { + names: [ + "front-history-importing", + 'fronthistoryimporting', + 'fronthistory-importing', + 'front-historyimporting', + 'fronthistoryimport', + 'fronthistory-import', + 'front-historyimport', + 'front-history', + 'fronthistory', + 'front-importing', + 'frontimporting', + 'front-import', + 'frontimport', + 'fhi', + ], + description: 'Talks about importing the front history from PluralKit.', + title: "Front history importing", + text: + 'Importing your front history to and from PluralKit is planned in the future, but is currently ' + + 'impossible to give any front entries to or take entries from PluralKit until APIv2 for PluralKit is ' + + 'finished. This feature also takes a lower priority than features unique to Simply Plural.' + }, + { + names: [ + "messaging", + ], + description: 'Talks about the possibility of having a messaging feature.', + title: "Messaging", + text: + 'The feature of messaging other systems within the app is out of scope, the app is not meant to be a ' + + 'social community app but a tool for you and your friends. Adding messages between systems would need ' + + 'us to implement moderation tools, moderation team and the actual feature, which is not the direction ' + + 'we are taking the app in right now.\n' + + '\n' + + 'Messaging within the system, between headmates, is planned for the future so you can communicate more ' + + 'easily within the system.' + }, + { + names: [ + "notifications", + "notification", + ], + description: 'Explains how your friends can get notifications from your system.', + title: "Notifications", + text: + 'If your friends are not getting notifications, make sure that you go into the settings of the friend ' + + 'by going on their profile and clicking the cog wheel on the right top. Press "They can get ' + + 'notifications". As a second step your friend(s) have to opt-in to get notifications from you, they ' + + 'have to go to your profile in their friends and click "Get notifications if they change front".' + }, + { + names: [ + "see-members", + "seemembers", + "seembmer", + "sm", + ], + description: 'Explains how to allow your friends to see your members.', + title: "See members", + text: + 'If your friends cannot see your members, you have to go into the friend their profile, click on the ' + + 'cogwheel on the right top and press "They can see your shared members", this will allow them to see ' + + 'your public members' + }, + { + names: [ + 'sync-members-pluralkit', + 'sync-member-pluralkit', + 'syncmemberpluralkit', + 'syncmember-pluralkit', + 'sync-memberpluralkit', + 'syncmemberspluralkit', + 'syncmembers-pluralkit', + 'sync-memberspluralkit', + 'sync-member-plural-kit', + 'syncmemberplural-kit', + 'syncmember-plural-kit', + 'sync-memberplural-kit', + 'syncmembersplural-kit', + 'syncmembers-plural-kit', + 'sync-membersplural-kit', + 'sync-member', + 'syncmember', + 'sync-members', + 'syncmembers', + 'sync-pluralkit', + 'syncpluralkit', + 'sync-plural-kit', + 'syncplural-kit', + 'smp', + 'smpk', + ], + description: 'Talks about syncing your members to PluralKit.', + title: "Sync members to PluralKit", + text: + 'If you wish to sync your members to PluralKit, go into the settings page -> Integrations -> PluralKit ' + + 'and fill in your PluralKit token, you can get this token by typing pk;token anywhere and PluralKit ' + + 'will message you the token in a DM. \n' + + '\n' + + 'Once filled out, you can go to actions in the members page and press Sync (rebooting app may be ' + + 'required to see this option after adding the token). You will be prompted with the option to sync to ' + + 'and from pk. \n' + + '\n' + + 'Pay attention that they are linked by the plural kit id found in the individual member settings in ' + + 'Simply Plural. If you make a member on Simply Plural and you make the same member on PluralKit you ' + + 'will have to go into the individual member settings of Simply Plural and fill in the PluralKit user id ' + + 'in the settings. If you don\'t do this you will end up with duplicate members on Plural Kit.' + }, + { + names: [ + "system-relationsips", + "system-relationship", + "systemrelationships", + "sr", + ], + description: 'Explains what "system relationships" are.', + title: "System relationships", + text: + 'This is an open-ended field meant to describe what relationships a headmate has. It can be used to ' + + 'describe the member\'s relationship to the system as a whole, their inner system relationships such as ' + + 'being family, friends, or romantic partners, or it can describe their outer-system relationships with ' + + 'people in the outer world, such as family, friends, and romantic partners.' + }, + { + names: [ + "website", + ], + title: "Website", + description: 'Talks about the possibility of having a web portal for Simply Plural.', + text: + 'A web portal for Simply Plural is unlikely to be made at the moment. The framework that Simply Plural ' + + 'has been created with has the capability of web development, but it is currently experimental, which ' + + 'would make it buggy and unstable to use. This decision may change later in the year when the framework ' + + 'becomes more stable and once the app is more complete.' + } +] \ No newline at end of file diff --git a/model/command-category.js b/model/command-category.js index f7091f5..6e2f204 100644 --- a/model/command-category.js +++ b/model/command-category.js @@ -1,10 +1,7 @@ const CommandCategory = { MODERATION: 'moderation', - ADMINISTRATION: 'administration', BOT_MANAGEMENT: 'bot_management', FUN: 'fun', - INFO: 'info', - ROLE: 'role', RESOURCE: 'resource' }; diff --git a/model/command.js b/model/command.js index c61c311..5d37000 100644 --- a/model/command.js +++ b/model/command.js @@ -3,6 +3,8 @@ const Discord = require('discord.js'); const Config = require('../config.json'); const Guild = require('./guild'); +const customMessages = require("./messages"); + const cachelessRequire = (path) => { if (typeof path === 'string') { delete require.cache[require.resolve(path)]; @@ -44,37 +46,32 @@ const Command = { * @returns {boolean} */ parseMessage: async (message) => { - let isCommand = false; + if (!message.content.toLowerCase().startsWith(Config.prefix)) + return false; - if (message.content.toLowerCase().substr(0, Config.prefix.length) === Config.prefix) { - let content = message.content.substr(Config.prefix.length).trim().split(' '); - const calledCommand = content.shift().toLowerCase(); + let content = message.content.substr(Config.prefix.length).trim().split(' '); + const calledCommand = content.shift().toLowerCase(); - if (await Command.isValid(calledCommand, message)) { - const member = await Guild.getMemberFromMessage(message); + if (embed = customMessages.getEmbed(calledCommand)) + return await message.channel.send({embed}); - if (member === null) { - message.reply('sorry, you do not seem to be on the server.'); - } else { - let commandName = calledCommand; - isCommand = true; + if (!await Command.isValid(calledCommand, message)) return; + + const member = await Guild.getMemberFromMessage(message); + if (member === null) + return message.reply('sorry, you do not seem to be on the server.'); - if (Command.commandAliases.hasOwnProperty(calledCommand)) { - commandName = Command.commandAliases[calledCommand]; - } + let commandName = calledCommand; - const commandInstance = cachelessRequire(Command.commandList.get(commandName)); + if (Command.commandAliases.hasOwnProperty(calledCommand)) + commandName = Command.commandAliases[calledCommand]; - if (commandInstance !== null) { - commandInstance.process(message, content, Command); - } else { - Command.commandList.delete(commandName); - } - } - } - } + const commandInstance = cachelessRequire(Command.commandList.get(commandName)); - return isCommand; + if (commandInstance !== null) + commandInstance.process(message, content, Command); + else + Command.commandList.delete(commandName); }, /** diff --git a/model/command/add-friend.js b/model/command/add-friend.js deleted file mode 100644 index e59f190..0000000 --- a/model/command/add-friend.js +++ /dev/null @@ -1,40 +0,0 @@ -const { MessageEmbed } = require('discord.js'); -const CommandCategory = require('../command-category'); -const CommandPermission = require('../command-permission'); - -class AddFriend -{ - static instance = null; - - constructor() { - if (AddFriend.instance !== null) { - return AddFriend.instance; - } - - this.aliases = ['add-friends', 'addfriends', 'addfriend', 'af']; - this.category = CommandCategory.RESOURCE; - this.isAllowedForContext = CommandPermission.yes; - this.description = 'Explains how to troubleshoot not being able to add a friend.'; - } - - /** - * @param {Message} message - * @param {Array} args - */ - async process(message, args) { - const embed = new MessageEmbed(); - - embed.setColor(APP_MAIN_COLOUR); - embed.setAuthor('Add friend', bot.user.displayAvatarURL({ dynamic: true })); - embed.setDescription( - 'If you and your friends are unable to add each other as friends, or can\'t find each other as friends, ' + - 'make sure that you are using a case sensitive username when adding each other. Additionally, it is ' + - 'currently not possible to add people by their user ID, so if you are using a user id to add them as ' + - 'friend this currently does not work but will in the future.' - ); - - return message.channel.send(embed); - } -} - -module.exports = new AddFriend(); diff --git a/model/command/add-set-front.js b/model/command/add-set-front.js deleted file mode 100644 index 02a95d1..0000000 --- a/model/command/add-set-front.js +++ /dev/null @@ -1,52 +0,0 @@ -const { MessageEmbed } = require('discord.js'); -const CommandCategory = require('../command-category'); -const CommandPermission = require('../command-permission'); - -class AddSetFront -{ - static instance = null; - - constructor() { - if (AddSetFront.instance !== null) { - return AddSetFront.instance; - } - - this.aliases = [ - 'addsetfront', - 'addset-front', - 'add-setfront', - 'add-set', - 'addset', - 'add-front', - 'addfront', - 'set-front', - 'setfront', - 'asf', - ]; - this.category = CommandCategory.RESOURCE; - this.isAllowedForContext = CommandPermission.yes; - this.description = 'Talks about how to set who is fronting in the application.'; - } - - /** - * @param {Message} message - * @param {Array} args - */ - async process(message, args) { - const embed = new MessageEmbed(); - - embed.setColor(APP_MAIN_COLOUR); - embed.setAuthor('Add / set front', bot.user.displayAvatarURL({ dynamic: true })); - embed.setDescription( - 'There are two ways in-app to show a member as fronting. Set as front, and add to front.\n' + - '\n' + - 'Using set as front will clear all members from your front list, and then add that member. Add to front ' + - 'won\'t remove anyone from the list, but it will add the member whose add icon you tapped. You can ' + - 'individually remove members by tapping the downward facing arrow next to their name.' - ); - - return message.channel.send(embed); - } -} - -module.exports = new AddSetFront(); diff --git a/model/command/custom-fields.js b/model/command/custom-fields.js deleted file mode 100644 index 1f88830..0000000 --- a/model/command/custom-fields.js +++ /dev/null @@ -1,39 +0,0 @@ -const { MessageEmbed } = require('discord.js'); -const CommandCategory = require('../command-category'); -const CommandPermission = require('../command-permission'); - -class CustomFields -{ - static instance = null; - - constructor() { - if (CustomFields.instance !== null) { - return CustomFields.instance; - } - - this.aliases = ['custom-fields', 'customfields', 'customfield', 'cfi']; - this.category = CommandCategory.RESOURCE; - this.isAllowedForContext = CommandPermission.yes; - this.description = 'Talks about the "custom fields" feature.'; - } - - /** - * @param {Message} message - * @param {Array} args - */ - async process(message, args) { - const embed = new MessageEmbed(); - - embed.setColor(APP_MAIN_COLOUR); - embed.setAuthor('Custom fields', bot.user.displayAvatarURL({ dynamic: true })); - embed.setDescription( - 'The ability to change the fields shown on your member lists is planned. This feature will also bring ' + - 'the ability to remove and add fields, as well as make fields private, public, or show them to only ' + - 'trusted friends.' - ); - - return message.channel.send(embed); - } -} - -module.exports = new CustomFields(); diff --git a/model/command/custom-front.js b/model/command/custom-front.js deleted file mode 100644 index 6ccb019..0000000 --- a/model/command/custom-front.js +++ /dev/null @@ -1,42 +0,0 @@ -const { MessageEmbed } = require('discord.js'); -const CommandCategory = require('../command-category'); -const CommandPermission = require('../command-permission'); - -class CustomFront -{ - static instance = null; - - constructor() { - if (CustomFront.instance !== null) { - return CustomFront.instance; - } - - this.aliases = ['custom-fronts', 'customfronts', 'customfront', 'cf']; - this.category = CommandCategory.RESOURCE; - this.isAllowedForContext = CommandPermission.yes; - this.description = 'Explains what "custom front" is.'; - } - - /** - * @param {Message} message - * @param {Array} args - */ - async process(message, args) { - const embed = new MessageEmbed(); - - embed.setColor(APP_MAIN_COLOUR); - embed.setAuthor('Custom fronts', bot.user.displayAvatarURL({ dynamic: true })); - embed.setDescription( - 'Custom fronts is a kind of status for fronts, like "blurred", "unknown member", "dissociated", etc... \n' + - '\n' + - 'You don\'t want those to show up as real members in your system list but you still want to be able to ' + - 'set front as one of them — that\'s where custom fronts kick in.\n' + - '\n' + - 'They\'re highly customizable (as per popular request) so you can name them anything you want.' - ); - - return message.channel.send(embed); - } -} - -module.exports = new CustomFront(); diff --git a/model/command/embed.js b/model/command/embed.js index bd463c9..03559e6 100644 --- a/model/command/embed.js +++ b/model/command/embed.js @@ -140,7 +140,7 @@ class Embed } this.aliases = []; - this.category = CommandCategory.RESOURCE; + this.category = CommandCategory.MODERATION; this.isAllowedForContext = CommandPermission.isMemberModOrHelper; this.description = 'Allows to post an embed'; } diff --git a/model/command/front-history-importing.js b/model/command/front-history-importing.js deleted file mode 100644 index f7212c3..0000000 --- a/model/command/front-history-importing.js +++ /dev/null @@ -1,53 +0,0 @@ -const { MessageEmbed } = require('discord.js'); -const CommandCategory = require('../command-category'); -const CommandPermission = require('../command-permission'); - -class FrontHistoryImporting -{ - static instance = null; - - constructor() { - if (FrontHistoryImporting.instance !== null) { - return FrontHistoryImporting.instance; - } - - this.aliases = [ - 'fronthistoryimporting', - 'fronthistory-importing', - 'front-historyimporting', - 'fronthistoryimport', - 'fronthistory-import', - 'front-historyimport', - 'front-history', - 'fronthistory', - 'front-importing', - 'frontimporting', - 'front-import', - 'frontimport', - 'fhi', - ]; - this.category = CommandCategory.RESOURCE; - this.isAllowedForContext = CommandPermission.yes; - this.description = 'Talks about importing the front history from PluralKit.'; - } - - /** - * @param {Message} message - * @param {Array} args - */ - async process(message, args) { - const embed = new MessageEmbed(); - - embed.setColor(APP_MAIN_COLOUR); - embed.setAuthor('Front history importing', bot.user.displayAvatarURL({ dynamic: true })); - embed.setDescription( - 'Importing your front history to and from PluralKit is planned in the future, but is currently ' + - 'impossible to give any front entries to or take entries from PluralKit until APIv2 for PluralKit is ' + - 'finished. This feature also takes a lower priority than features unique to Simply Plural.' - ); - - return message.channel.send(embed); - } -} - -module.exports = new FrontHistoryImporting(); diff --git a/model/command/help.js b/model/command/help.js index d5e9722..4cd45e5 100644 --- a/model/command/help.js +++ b/model/command/help.js @@ -1,184 +1,89 @@ -const Discord = require('discord.js'); -const Logger = require('@lilywonhalf/pretty-logger'); -const Config = require('../../config.json'); -const EmojiCharacters = require('../../emoji-characters.json'); -const Guild = require('../guild'); -const CommandCategory = require('../command-category'); const CommandPermission = require('../command-permission'); +const CommandCategory = require('../command-category'); + +const commands = require('../command').commandList; +const messages = require('../messages'); const cachelessRequire = (path) => { - if (typeof path === 'string') { + if (!typeof path === 'string') { delete require.cache[require.resolve(path)]; } return typeof path === 'string' ? require(path) : null; }; -class HelpDialog -{ - /** - * @param {Message} message - */ - constructor(message) { - this.categoriesMapping = new Discord.Collection(); - this.categoriesEmbed = new Discord.MessageEmbed(); - this.originalMessage = message; - this.categoryCommandMapping = new Discord.Collection(); - this.categoryColourMapping = new Discord.Collection(); - this.postedMessage = null; - this.usedEmojis = []; - this.stopAddingReactions = false; +const cleanString = (str) => str === null || str === undefined ? null : str + .replace("_", " ") + .split(" ") + .map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()) + .join(" "); - this.categoriesEmbed.setColor(APP_MAIN_COLOUR); - this.categoriesEmbed.setFooter('Click on one of the reactions below'); +const mainPage = (id) => { + const buttons = Object.keys(CommandCategory).map(c => ({ + type: 2, + style: 1, + label: cleanString(c), + custom_id: `help-${id}-${c}`, + })) - for (let category of Object.values(CommandCategory)) { - this.categoryColourMapping.set(category, APP_MAIN_COLOUR); - } - - this.categoryColourMapping.set(CommandCategory.RESOURCE, 'fdfd96'); + return { + embeds: [{ + title: "Help command", + color: 0xA95B44, + description: + 'Spot stands for **S**imply **P**lural b**ot**. This bot is mainly used to generate changelogs and help ' + + 'with the definition of features, so recurring questions can be answered more quickly.\n' + + '\n' + + 'Commands are classified by categories because displaying every command in this small box would be ' + + 'confusing and would break Discord\'s character limit. Click on a buttom below to show the commands ' + + 'available in the corresponding category, or type `.help `.\n' + }], + components: [{ + "type": 1, + "components": buttons, + }] } - /** - * @param {Command} Command - */ - async init(Command) { - const callableCommands = new Discord.Collection(); - const commandList = Command.commandList.keyArray(); +} - for (let i = 0; i < commandList.length; i++) { - const commandName = commandList[i]; - const command = cachelessRequire(`../${Command.commandList.get(commandName)}`); - const isAllowed = await command.isAllowedForContext(this.originalMessage); +const categoryPage = (cat, id, direct = false) => { + const c = Object.values(CommandCategory).map(x => cleanString(x).toLowerCase()); + if (!c.includes(cat.toLowerCase())) + return; - if (isAllowed) { - callableCommands.set(commandName, command); - } - } + let data = { + embeds: [{ + title: `Help command | ${cleanString(cat)}`, + color: 0xA95B44, + }], + components: [{ + "type": 1, + "components": [{ + type: 2, + style: 2, + label: "Back", + custom_id: `help-${id}-home`, + }], + }] + }; - callableCommands.forEach((command, commandName) => { - if (!this.categoryCommandMapping.has(command.category)) { - this.categoryCommandMapping.set(command.category, new Discord.Collection()); - } + if (direct) delete data.components; - this.categoryCommandMapping.get(command.category).set(commandName, command); - }); + if (cat.toLowerCase() == cleanString(CommandCategory.RESOURCE).toLowerCase()) + data.embeds[0].fields = messages.getList(); + else + data.embeds[0].fields = Array.from(commands.keys()).map(x => { + const cmd = cachelessRequire(commands.get(x).split("command/").join("")); + if (cleanString(cmd.category)?.toLowerCase() != cat.toLowerCase()) return; + return { name: x, value: cmd.description ?? "No description."}; + }).filter(x => x); - let i = 1; - - const categories = callableCommands.reduce((accumulator, command) => { - if (!this.categoriesMapping.array().includes(command.category)) { - let commandCategory = command.category.replace('_', ' '); - commandCategory = `${commandCategory.slice(0, 1).toUpperCase()}${commandCategory.slice(1)}`; - - this.categoriesMapping.set(EmojiCharacters[i], command.category); - this.usedEmojis.push(EmojiCharacters[i]); - accumulator += `${EmojiCharacters[i]} ${commandCategory}\n`; - i++; - } - - return accumulator; - }, ''); - - this.categoriesEmbed.setTitle('Help command'); - this.categoriesEmbed.setDescription( - 'Spot stands for **S**imply **P**lural b**ot**. This bot is mainly used to generate changelogs and help ' + - 'with the definition of features, so recurring questions can be answered more quickly.\n' + - '\n' + - 'Commands are classified by categories because displaying every command in this small box would be ' + - 'confusing and would break Discord\'s character limit. Click on a reaction to show the commands ' + - 'available in the corresponding category.\n' + - '\n' + - categories - ); - - return this.listCategories(); - } - - /** - * @param {Collection} [collection] - */ - async listCategories(collection) { - this.stopAddingReactions = true; - - if (collection !== undefined && collection.size < 1) { - await this.postedMessage.reactions.removeAll().catch(error => Logger.warning(error.message)); - return; - } - - const member = await Guild.getMemberFromMessage(this.originalMessage); - const filter = (reaction, user) => { - const emoji = reaction.emoji.name; - - return this.usedEmojis.includes(emoji) && user.id === member.user.id; - }; - - if (this.postedMessage === null) { - this.postedMessage = await this.originalMessage.channel.send(this.categoriesEmbed).catch(error => Logger.warning(error.toString())); - } else { - await this.postedMessage.reactions.removeAll().catch(error => Logger.warning(error.message)); - await this.postedMessage.edit('', this.categoriesEmbed); - } - - // 5 minutes - this.postedMessage.awaitReactions(filter, { time: 300000, max: 1 }).then(this.listCommands.bind(this)).catch(Logger.exception); - this.stopAddingReactions = false; - - for (let i = 0; i < this.usedEmojis.length && !this.stopAddingReactions; i++) { - await this.postedMessage.react(this.usedEmojis[i]).catch(error => Logger.warning(error.message)); - } - } - - /** - * @param {Collection} collection - */ - async listCommands(collection) { - this.stopAddingReactions = true; - await this.postedMessage.reactions.removeAll().catch(error => Logger.warning(error.message)); - - if (collection.size < 1) { - this.postedMessage.edit('Timed out! You may send the command again.'); - return; - } - - const member = await Guild.getMemberFromMessage(this.originalMessage); - const filter = (reaction, user) => { - const emoji = reaction.emoji.name; - - return emoji === '↩️' && user.id === member.user.id; - }; - - const category = this.categoriesMapping.get(collection.first().emoji.name); - const commandsEmbed = new Discord.MessageEmbed(); - const commands = this.categoryCommandMapping.get(category).reduce((accumulator, command, commandName) => { - accumulator += `**${Config.prefix}${commandName}** ${command.description}\n\n`; - - return accumulator; - }, ''); - - commandsEmbed.setTitle('Help command'); - commandsEmbed.setDescription(commands); - commandsEmbed.setColor(this.categoryColourMapping.get(category)); - commandsEmbed.setFooter('Click on one of the reactions below'); - - this.postedMessage.edit('', commandsEmbed); - - // 5 minutes - this.postedMessage.awaitReactions(filter, { time: 300000, max: 1 }).then(this.listCategories.bind(this)).catch(Logger.exception); - - await this.postedMessage.react('↩️').catch(error => Logger.warning(error.message)); - } + return data; } class Help { - static instance = null; - constructor() { - if (Help.instance !== null) { - return Help.instance; - } - this.aliases = ['commands']; this.category = CommandCategory.INFO; this.isAllowedForContext = CommandPermission.yes; @@ -190,11 +95,45 @@ class Help * @param {Array} args * @param {Command} Command */ - async process(message, args, Command) { - const dialog = new HelpDialog(message); - - return dialog.init(Command); + async process(message, args, command) { + if (!args[0]) + await bot.api.channels(message.channel.id).messages.post({ data: mainPage(message.author.id) }); + else + await bot.api.channels(message.channel.id).messages.post({ + data: + categoryPage(args.join(" "), message.author.id, true) + ?? { + content: "Category not found.", + message_reference: { message_id: message.id, guild_id: message.guild?.id }, + allowed_mentions: { parse: [] }, + } + }) } } module.exports = new Help(); + +module.exports.interactionHandler = async (event) => { + const user = event.member.user.id; + const custom_id = event.data.custom_id; + + let ret; + + if (!custom_id.startsWith(`help-${user}`)) + ret = { + type: 4, + data: { + flags: 64, + content: "This help command was sent by someone else. Please run `.help` again.", + }, + } + else if (custom_id.endsWith('home')) + ret = { type: 7, data: mainPage(user) }; + else { + let page = categoryPage(cleanString(custom_id.split(`help-${user}-`).join("")), user); + if (page) ret = { type: 7, data: page }; + else ret = { type: 4, data: { flags: 64, content: "Category not found." }} + } + + return await bot.api.interactions(event.id, event.token).callback.post({ data: ret }) +} diff --git a/model/command/messaging.js b/model/command/messaging.js deleted file mode 100644 index f665b5a..0000000 --- a/model/command/messaging.js +++ /dev/null @@ -1,43 +0,0 @@ -const { MessageEmbed } = require('discord.js'); -const CommandCategory = require('../command-category'); -const CommandPermission = require('../command-permission'); - -class Messaging -{ - static instance = null; - - constructor() { - if (Messaging.instance !== null) { - return Messaging.instance; - } - - this.aliases = []; - this.category = CommandCategory.RESOURCE; - this.isAllowedForContext = CommandPermission.yes; - this.description = 'Talks about the possibility of having a messaging feature.'; - } - - /** - * @param {Message} message - * @param {Array} args - */ - async process(message, args) { - const embed = new MessageEmbed(); - - embed.setColor(APP_MAIN_COLOUR); - embed.setAuthor('Messaging', bot.user.displayAvatarURL({ dynamic: true })); - embed.setDescription( - 'The feature of messaging other systems within the app is out of scope, the app is not meant to be a ' + - 'social community app but a tool for you and your friends. Adding messages between systems would need ' + - 'us to implement moderation tools, moderation team and the actual feature, which is not the direction ' + - 'we are taking the app in right now.\n' + - '\n' + - 'Messaging within the system, between headmates, is planned for the future so you can communicate more ' + - 'easily within the system.' - ); - - return message.channel.send(embed); - } -} - -module.exports = new Messaging(); diff --git a/model/command/notifications.js b/model/command/notifications.js deleted file mode 100644 index d5e91e3..0000000 --- a/model/command/notifications.js +++ /dev/null @@ -1,40 +0,0 @@ -const { MessageEmbed } = require('discord.js'); -const CommandCategory = require('../command-category'); -const CommandPermission = require('../command-permission'); - -class Notifications -{ - static instance = null; - - constructor() { - if (Notifications.instance !== null) { - return Notifications.instance; - } - - this.aliases = ['notification']; - this.category = CommandCategory.RESOURCE; - this.isAllowedForContext = CommandPermission.yes; - this.description = 'Explains how your friends can get notifications from your system.'; - } - - /** - * @param {Message} message - * @param {Array} args - */ - async process(message, args) { - const embed = new MessageEmbed(); - - embed.setColor(APP_MAIN_COLOUR); - embed.setAuthor('Notifications', bot.user.displayAvatarURL({ dynamic: true })); - embed.setDescription( - 'If your friends are not getting notifications, make sure that you go into the settings of the friend ' + - 'by going on their profile and clicking the cog wheel on the right top. Press "They can get ' + - 'notifications". As a second step your friend(s) have to opt-in to get notifications from you, they ' + - 'have to go to your profile in their friends and click "Get notifications if they change front".' - ); - - return message.channel.send(embed); - } -} - -module.exports = new Notifications(); diff --git a/model/command/see-members.js b/model/command/see-members.js deleted file mode 100644 index 8a9d5af..0000000 --- a/model/command/see-members.js +++ /dev/null @@ -1,39 +0,0 @@ -const { MessageEmbed } = require('discord.js'); -const CommandCategory = require('../command-category'); -const CommandPermission = require('../command-permission'); - -class SeeMembers -{ - static instance = null; - - constructor() { - if (SeeMembers.instance !== null) { - return SeeMembers.instance; - } - - this.aliases = ['see-members', 'seemembers', 'seemember', 'sm']; - this.category = CommandCategory.RESOURCE; - this.isAllowedForContext = CommandPermission.yes; - this.description = 'Explains how to allow your friends to see your members.'; - } - - /** - * @param {Message} message - * @param {Array} args - */ - async process(message, args) { - const embed = new MessageEmbed(); - - embed.setColor(APP_MAIN_COLOUR); - embed.setAuthor('See members', bot.user.displayAvatarURL({ dynamic: true })); - embed.setDescription( - 'If your friends cannot see your members, you have to go into the friend their profile, click on the ' + - 'cogwheel on the right top and press "They can see your shared members", this will allow them to see ' + - 'your public members' - ); - - return message.channel.send(embed); - } -} - -module.exports = new SeeMembers(); diff --git a/model/command/sync-members-pluralkit.js b/model/command/sync-members-pluralkit.js deleted file mode 100644 index fbc63cc..0000000 --- a/model/command/sync-members-pluralkit.js +++ /dev/null @@ -1,73 +0,0 @@ -const { MessageEmbed } = require('discord.js'); -const CommandCategory = require('../command-category'); -const CommandPermission = require('../command-permission'); - -class SyncMembersPluralkit -{ - static instance = null; - - constructor() { - if (SyncMembersPluralkit.instance !== null) { - return SyncMembersPluralkit.instance; - } - - this.aliases = [ - 'sync-member-pluralkit', - 'syncmemberpluralkit', - 'syncmember-pluralkit', - 'sync-memberpluralkit', - 'syncmemberspluralkit', - 'syncmembers-pluralkit', - 'sync-memberspluralkit', - 'sync-member-plural-kit', - 'syncmemberplural-kit', - 'syncmember-plural-kit', - 'sync-memberplural-kit', - 'syncmembersplural-kit', - 'syncmembers-plural-kit', - 'sync-membersplural-kit', - 'sync-member', - 'syncmember', - 'sync-members', - 'syncmembers', - 'sync-pluralkit', - 'syncpluralkit', - 'sync-plural-kit', - 'syncplural-kit', - 'smp', - 'smpk', - ]; - this.category = CommandCategory.RESOURCE; - this.isAllowedForContext = CommandPermission.yes; - this.description = 'Talks about syncing your members to PluralKit.'; - } - - /** - * @param {Message} message - * @param {Array} args - */ - async process(message, args) { - const embed = new MessageEmbed(); - - embed.setColor(APP_MAIN_COLOUR); - embed.setAuthor('Sync members to PluralKit', bot.user.displayAvatarURL({ dynamic: true })); - embed.setDescription( - 'If you wish to sync your members to PluralKit, go into the settings page -> Integrations -> PluralKit ' + - 'and fill in your PluralKit token, you can get this token by typing pk;token anywhere and PluralKit ' + - 'will message you the token in a DM. \n' + - '\n' + - 'Once filled out, you can go to actions in the members page and press Sync (rebooting app may be ' + - 'required to see this option after adding the token). You will be prompted with the option to sync to ' + - 'and from pk. \n' + - '\n' + - 'Pay attention that they are linked by the plural kit id found in the individual member settings in ' + - 'Simply Plural. If you make a member on Simply Plural and you make the same member on PluralKit you ' + - 'will have to go into the individual member settings of Simply Plural and fill in the PluralKit user id ' + - 'in the settings. If you don\'t do this you will end up with duplicate members on Plural Kit.' - ); - - return message.channel.send(embed); - } -} - -module.exports = new SyncMembersPluralkit(); diff --git a/model/command/system-relationships.js b/model/command/system-relationships.js deleted file mode 100644 index 62da561..0000000 --- a/model/command/system-relationships.js +++ /dev/null @@ -1,40 +0,0 @@ -const { MessageEmbed } = require('discord.js'); -const CommandCategory = require('../command-category'); -const CommandPermission = require('../command-permission'); - -class SystemRelationships -{ - static instance = null; - - constructor() { - if (SystemRelationships.instance !== null) { - return SystemRelationships.instance; - } - - this.aliases = ['system-relationship', 'systemrelationships', 'systemrelationships', 'sr']; - this.category = CommandCategory.RESOURCE; - this.isAllowedForContext = CommandPermission.yes; - this.description = 'Explains what "system relationships" are.'; - } - - /** - * @param {Message} message - * @param {Array} args - */ - async process(message, args) { - const embed = new MessageEmbed(); - - embed.setColor(APP_MAIN_COLOUR); - embed.setAuthor('System relationships', bot.user.displayAvatarURL({ dynamic: true })); - embed.setDescription( - 'This is an open-ended field meant to describe what relationships a headmate has. It can be used to ' + - 'describe the member\'s relationship to the system as a whole, their inner system relationships such as ' + - 'being family, friends, or romantic partners, or it can describe their outer-system relationships with ' + - 'people in the outer world, such as family, friends, and romantic partners.' - ); - - return message.channel.send(embed); - } -} - -module.exports = new SystemRelationships(); diff --git a/model/command/website.js b/model/command/website.js deleted file mode 100644 index 2d109bd..0000000 --- a/model/command/website.js +++ /dev/null @@ -1,40 +0,0 @@ -const { MessageEmbed } = require('discord.js'); -const CommandCategory = require('../command-category'); -const CommandPermission = require('../command-permission'); - -class Website -{ - static instance = null; - - constructor() { - if (Website.instance !== null) { - return Website.instance; - } - - this.aliases = []; - this.category = CommandCategory.RESOURCE; - this.isAllowedForContext = CommandPermission.yes; - this.description = 'Talks about the possibility of having a web portal for Simply Plural.'; - } - - /** - * @param {Message} message - * @param {Array} args - */ - async process(message, args) { - const embed = new MessageEmbed(); - - embed.setColor(APP_MAIN_COLOUR); - embed.setAuthor('Website', bot.user.displayAvatarURL({ dynamic: true })); - embed.setDescription( - 'A web portal for Simply Plural is unlikely to be made at the moment. The framework that Simply Plural ' + - 'has been created with has the capability of web development, but it is currently experimental, which ' + - 'would make it buggy and unstable to use. This decision may change later in the year when the framework ' + - 'becomes more stable and once the app is more complete.' - ); - - return message.channel.send(embed); - } -} - -module.exports = new Website(); diff --git a/model/messages.js b/model/messages.js new file mode 100644 index 0000000..7173da9 --- /dev/null +++ b/model/messages.js @@ -0,0 +1,54 @@ +let names = {}; +let messages = {}; + +const genRandomId = () => Math.ceil(Math.random() * 1000); + +const load = () => require("../messages").forEach(msg => { + let id = genRandomId(); + while (messages[id]) + id = genRandomId(); + + msg.names.forEach(name => names[name] = id); + messages[id] = msg; +}); + +load(); + +// interface Message { +// names string[]; +// description string; +// title string; +// text string; +// } + +const { MessageEmbed } = require("discord.js"); + +module.exports = { + get names() { + return names; + }, + get messages() { + return messages; + }, + get: (name) => messages[names[name]], + getList: () => Object.keys(messages).map(msg => ({ name: messages[msg].names[0], value: messages[msg].description })), + getEmbed: (name) => { + let msg = messages[names[name]]; + if (!msg) return; + + return { + color: APP_MAIN_COLOUR, + author: { + name: msg.title, + iconURL: bot.user.displayAvatarURL({ dynamic: true }), + }, + description: msg.text, + } + }, + reload: () => { + names = {}; + messages = {}; + delete require.cache[require.resolve("../messages")]; + load(); + } +}