From 22e03d23357ce3177becdefaa4e0e22888bc95a9 Mon Sep 17 00:00:00 2001 From: Dragory Date: Mon, 15 Apr 2019 19:06:59 +0300 Subject: [PATCH] Rename requiredJoinedAt -> requiredTimeOnServer, add support for multiple main guilds for that setting --- README.md | 4 +-- src/config.js | 4 +-- src/data/threads.js | 69 +++++++++++++++++++++++----------------- src/main.js | 15 +-------- src/modules/newthread.js | 13 +------- 5 files changed, 46 insertions(+), 59 deletions(-) diff --git a/README.md b/README.md index 472888d..26eb1f3 100644 --- a/README.md +++ b/README.md @@ -81,7 +81,7 @@ These go in `config.json`. See also `config.example.json`. |greetingMessage|None|Text content of the welcome message| |ignoreAccidentalThreads|false|If set to true, the bot attempts to ignore common "accidental" messages that would start a new thread, such as "ok", "thanks", etc.| |inboxServerPermission|None|Permission required to use bot commands on the inbox server| -|joinedAtDeniedMessage|"You haven't been a member of the server for enough time to contact modmail"|See `requiredJoinedAt` below| +|timeOnServerDeniedMessage|"You haven't been a member of the server for long enough to contact modmail."|See `requiredTimeOnServer` below| |mentionRole|"here"|Role that is mentioned when new threads are created or the bot is mentioned. Accepted values are "here", "everyone", or a role id as a string. Set to null to disable these pings entirely.| |mentionUserInThreadHeader|false|If set to true, mentions the thread's user in the thread header| |newThreadCategoryId|None|ID of the category where new modmail thread channels should be placed| @@ -90,7 +90,7 @@ These go in `config.json`. See also `config.example.json`. |prefix|"!"|Prefix for bot commands| |relaySmallAttachmentsAsAttachments|false|Whether to relay small attachments from users as native attachments rather than links in modmail threads| |requiredAccountAge|None|Required account age for contacting modmail (in hours). If the account is not old enough, a new thread will not be created and the bot will reply with `accountAgeDeniedMessage` (if set) instead.| -|requiredJoinedAt|None|Required amount of time (in hours) the user must be a member of the server before being able to contact modmail. If the user hasn't been a member of the server for the specified time, a new thread will not be created and the bot will reply with `joinedAtDeniedMessage` (if set) instead. Only works if one main guild has been set.| +|requiredTimeOnServer|None|Required amount of time (in minutes) the user must be a member of the server before being able to contact modmail. If the user hasn't been a member of the server for the specified time, a new thread will not be created and the bot will reply with `timeOnServerDeniedMessage` (if set) instead.| |responseMessage|"Thank you for your message! Our mod team will reply to you here as soon as possible."|The bot's response to DMs that start a new thread| |smallAttachmentLimit|2097152|Size limit of `relaySmallAttachmentsAsAttachments`, in bytes (default is 2MB)| |snippetPrefix|"!!"|Prefix to use snippets| diff --git a/src/config.js b/src/config.js index 1f9f17a..8eb54d4 100644 --- a/src/config.js +++ b/src/config.js @@ -78,8 +78,8 @@ const defaultConfig = { "requiredAccountAge": null, // In hours "accountAgeDeniedMessage": "Your Discord account is not old enough to contact modmail.", - "requiredJoinedAt": null, // In hours too - "joinedAtDeniedMessage": "You haven't been a member of the server for enough time to contact modmail", + "requiredTimeOnServer": null, // In minutes + "timeOnServerDeniedMessage": "You haven't been a member of the server for long enough to contact modmail.", "relaySmallAttachmentsAsAttachments": false, "smallAttachmentLimit": 1024 * 1024 * 2, diff --git a/src/data/threads.js b/src/data/threads.js index 19b6622..a854363 100644 --- a/src/data/threads.js +++ b/src/data/threads.js @@ -1,4 +1,5 @@ -const Eris = require('eris'); +const {User, Member} = require('eris'); + const transliterate = require('transliteration'); const moment = require('moment'); const uuid = require('uuid'); @@ -12,6 +13,9 @@ const utils = require('../utils'); const Thread = require('./Thread'); const {THREAD_STATUS} = require('./constants'); +const MINUTES = 60 * 1000; +const HOURS = 60 * MINUTES; + /** * @param {String} id * @returns {Promise} @@ -46,33 +50,22 @@ function getHeaderGuildInfo(member) { /** * Creates a new modmail thread for the specified user - * @param {Eris.User} user + * @param {User} user + * @param {Member} member * @param {Boolean} quiet If true, doesn't ping mentionRole or reply with responseMessage - * @returns {Promise} + * @returns {Promise} * @throws {Error} */ -async function createNewThreadForUser(user, member, quiet = false) { +async function createNewThreadForUser(user, quiet = false) { const existingThread = await findOpenThreadByUserId(user.id); if (existingThread) { throw new Error('Attempted to create a new thread for a user with an existing open thread!'); } - // Check the config for a requirement of a minimum time the user must be a member of the guild to contact modmail, - // if the user hasn't been a member of the guild for the specified time, return an optional message without making a new thread - if (config.requiredJoinedAt && member) { - if (member.joinedAt > moment() - config.requiredJoinedAt) { - if (config.joinedAtDeniedMessage) { - const privateChannel = await user.getDMChannel(); - await privateChannel.createMessage(config.joinedAtDeniedMessage); - } - return; - } - } - - // Check the config for a requirement of account age to contact modmail, - // if the account is too young, return an optional message without making a new thread + // If set in config, check that the user's account is old enough (time since they registered on Discord) + // If the account is too new, don't start a new thread and optionally reply to them with a message if (config.requiredAccountAge) { - if (user.createdAt > moment() - config.requiredAccountAge * 3600000){ + if (user.createdAt > moment() - config.requiredAccountAge * HOURS){ if (config.accountAgeDeniedMessage) { const privateChannel = await user.getDMChannel(); await privateChannel.createMessage(config.accountAgeDeniedMessage); @@ -81,16 +74,6 @@ async function createNewThreadForUser(user, member, quiet = false) { } } - // Use the user's name+discrim for the thread channel's name - // Channel names are particularly picky about what characters they allow, so we gotta do some clean-up - let cleanName = transliterate.slugify(user.username); - if (cleanName === '') cleanName = 'unknown'; - cleanName = cleanName.slice(0, 95); // Make sure the discrim fits - - const channelName = `${cleanName}-${user.discriminator}`; - - console.log(`[NOTE] Creating new thread channel ${channelName}`); - // Find which main guilds this user is part of const mainGuilds = utils.getMainGuilds(); const userGuildData = new Map(); @@ -111,6 +94,34 @@ async function createNewThreadForUser(user, member, quiet = false) { } } + // If set in config, check that the user has been a member of one of the main guilds long enough + // If they haven't, don't start a new thread and optionally reply to them with a message + if (config.requiredTimeOnServer) { + // Check if the user joined any of the main servers a long enough time ago + // If we don't see this user on any of the main guilds (the size check below), assume we're just missing some data and give the user the benefit of the doubt + const isAllowed = userGuildData.size === 0 || Array.from(userGuildData.values()).some(({guild, member}) => { + return member.joinedAt < moment() - config.requiredTimeOnServer * MINUTES; + }); + + if (! isAllowed) { + if (config.timeOnServerDeniedMessage) { + const privateChannel = await user.getDMChannel(); + await privateChannel.createMessage(config.timeOnServerDeniedMessage); + } + return; + } + } + + // Use the user's name+discrim for the thread channel's name + // Channel names are particularly picky about what characters they allow, so we gotta do some clean-up + let cleanName = transliterate.slugify(user.username); + if (cleanName === '') cleanName = 'unknown'; + cleanName = cleanName.slice(0, 95); // Make sure the discrim fits + + const channelName = `${cleanName}-${user.discriminator}`; + + console.log(`[NOTE] Creating new thread channel ${channelName}`); + // Figure out which category we should place the thread channel in let newThreadCategoryId; diff --git a/src/main.js b/src/main.js index 43fa9fa..65667cd 100644 --- a/src/main.js +++ b/src/main.js @@ -74,25 +74,12 @@ bot.on('messageCreate', async msg => { messageQueue.add(async () => { let thread = await threads.findOpenThreadByUserId(msg.author.id); - // Due the way discord works, private channels don't have a member property as they are a guild only property, - // if more than one main guild have been set, we just ignore the requiredJoinedAt config option - let member; - const mainGuilds = utils.getMainGuilds(); - if (mainGuilds.length === 1) { - member = bot.guilds.get(mainGuilds[0]).members.get(msg.author.id); - if (! member) { - member = false; - } - } else { - member = false; - } - // New thread if (! thread) { // Ignore messages that shouldn't usually open new threads, such as "ok", "thanks", etc. if (config.ignoreAccidentalThreads && msg.content && ACCIDENTAL_THREAD_MESSAGES.includes(msg.content.trim().toLowerCase())) return; - thread = await threads.createNewThreadForUser(msg.author, member); + thread = await threads.createNewThreadForUser(msg.author); } if (thread) await thread.receiveUserReply(msg); diff --git a/src/modules/newthread.js b/src/modules/newthread.js index ac17951..ca3431d 100644 --- a/src/modules/newthread.js +++ b/src/modules/newthread.js @@ -17,24 +17,13 @@ module.exports = bot => { return; } - let member; - const mainGuilds = utils.getMainGuilds(); - if (mainGuilds.length === 1) { - member = bot.guilds.get(mainGuilds[0]).members.get(msg.author.id); - if (! member) { - member = false; - } - } else { - member = false; - } - const existingThread = await threads.findOpenThreadByUserId(user.id); if (existingThread) { utils.postSystemMessageWithFallback(msg.channel, thread, `Cannot create a new thread; there is another open thread with this user: <#${existingThread.channel_id}>`); return; } - const createdThread = await threads.createNewThreadForUser(user, member, true); + const createdThread = await threads.createNewThreadForUser(user, true); createdThread.postSystemMessage(`Thread was opened by ${msg.author.username}#${msg.author.discriminator}`); if (thread) {