From aec5f2a92cc381f98d49c99e3a6af0f534144d2b Mon Sep 17 00:00:00 2001 From: Hasan Latch Date: Fri, 26 Nov 2021 22:49:21 +0100 Subject: [PATCH] Added ability for bot to handle break role --- src/programs/menu/break-handler/addBreak.ts | 44 ++++++ .../menu/break-handler/removeBreak.ts | 61 ++++++++ src/programs/menu/common.ts | 65 +++++++++ src/programs/menu/dm-menu.ts | 131 +++--------------- src/programs/menu/nameChange.ts | 59 ++++++++ 5 files changed, 251 insertions(+), 109 deletions(-) create mode 100644 src/programs/menu/break-handler/addBreak.ts create mode 100644 src/programs/menu/break-handler/removeBreak.ts create mode 100644 src/programs/menu/common.ts create mode 100644 src/programs/menu/nameChange.ts diff --git a/src/programs/menu/break-handler/addBreak.ts b/src/programs/menu/break-handler/addBreak.ts new file mode 100644 index 000000000..fe6bc0165 --- /dev/null +++ b/src/programs/menu/break-handler/addBreak.ts @@ -0,0 +1,44 @@ +import prisma from "../../../prisma"; +import { GuildMember, DMChannel } from "discord.js"; +import { textLog } from "../../../common/moderator"; +import state from "../../../common/state"; +import Tools from "../../../common/tools"; +import { removeIgnore, logger, handleError, emojiCollector } from "../common"; + +export const addBreakRole = async ( + member: GuildMember, + dmChannel: DMChannel +) => { + const confirmationMessage = await dmChannel.send( + "It looks like you want a little break! It is understandable you can get it by clicking on the :sloth: emoji below. You can send me !menu again to remove it.\n**BE ADVISED: THIS COMMAND CAN ONLY BE USED EVERY 24 HOURS**" + ); + + state.ignoredGroupDMs.push(dmChannel.id); + + const breakRole = Tools.getRoleByName("Break", member.guild); + await confirmationMessage.react("🦥"); + + const reaction = await emojiCollector(confirmationMessage, "🦥"); + + if (reaction) { + try { + removeIgnore(dmChannel); + await confirmationMessage.delete(); + await member.roles.add(breakRole); + await prisma.usersOnBreak.create({ data: { userId: member.id } }); + dmChannel.send("Enjoy your break!"); + return; + } catch (e) { + logger.error("Failed to update break status", e); + removeIgnore(dmChannel); + await textLog(`I could not give <@${member.id}> the break role!`); + dmChannel.send( + "Looks like I couldn't give you the break role, I informed the Support team about it, in the meantime you can manually ask one of the Moderators!" + ); + return; + } + } + + removeIgnore(dmChannel); + await handleError(confirmationMessage, dmChannel); +}; diff --git a/src/programs/menu/break-handler/removeBreak.ts b/src/programs/menu/break-handler/removeBreak.ts new file mode 100644 index 000000000..c1d8aa882 --- /dev/null +++ b/src/programs/menu/break-handler/removeBreak.ts @@ -0,0 +1,61 @@ +import { UsersOnBreak } from "@yes-theory-fam/database/client"; +import prisma from "../../../prisma"; +import { GuildMember, DMChannel } from "discord.js"; +import { textLog } from "../../../common/moderator"; +import state from "../../../common/state"; +import Tools from "../../../common/tools"; +import { + isCooldownDone, + emojiCollector, + removeIgnore, + logger, + handleError, +} from "../common"; + +export const removeBreakRole = async ( + member: GuildMember, + userData: UsersOnBreak, + dmChannel: DMChannel +) => { + const checkTime = await isCooldownDone(userData); + + if (!checkTime) { + dmChannel.send( + "I'm sorry it hasn't been 24 hours since you used this command! If you really want the break role removed you can contact one of our moderators!" + ); + return; + } + + state.ignoredGroupDMs.push(dmChannel.id); + + const breakRole = Tools.getRoleByName("Break", member.guild); + const confirmationMessage = await dmChannel.send( + "It looks like you're ready to rejoin the server! Once you're ready click on the green check below!" + ); + await confirmationMessage.react("✅"); + + const reaction = await emojiCollector(confirmationMessage, "✅"); + + if (reaction) { + try { + await prisma.usersOnBreak.delete({ where: { userId: userData.userId } }); + await member.roles.remove(breakRole); + await dmChannel.send( + "Your break role was removed, we're happy to have you back!" + ); + removeIgnore(dmChannel); + return; + } catch (e) { + logger.error("Failed to update break status", e); + removeIgnore(dmChannel); + await textLog(`I could not remove <@${member.id}> break role!`); + await dmChannel.send( + "Looks like I had a little hiccup trying to remove your role, I've contacted Support about your little issue! Sorry in advance." + ); + return; + } + } + + removeIgnore(dmChannel); + await handleError(confirmationMessage, dmChannel); +}; diff --git a/src/programs/menu/common.ts b/src/programs/menu/common.ts new file mode 100644 index 000000000..75cdb7cca --- /dev/null +++ b/src/programs/menu/common.ts @@ -0,0 +1,65 @@ +import { UsersOnBreak } from "@yes-theory-fam/database/client"; +import prisma from "../../prisma"; +import { + Message, + DMChannel, + CollectorFilter, + MessageReaction, + User, +} from "discord.js"; +import state from "../../common/state"; +import { createYesBotLogger } from "../../log"; + +export const mainOptionsEmojis = ["👶", "🦥"]; +export const allCollectedEmojis = ["👶", "🦥", "✅", "🚫"]; + +export const logger = createYesBotLogger("programs", "DmMenu"); + +export const handleError = async ( + optionsMessage: Message, + dmChannel: DMChannel +) => { + removeIgnore(dmChannel); + + dmChannel.send( + "Because of technical reasons I can only wait 60 seconds for a reaction. I removed the other message to not confuse you. If you need anything from me, just drop me a message!" + ); + await optionsMessage.delete(); +}; + +export const isOnBreak = async (userId: string) => { + return await prisma.usersOnBreak.findFirst({ where: { userId } }); +}; + +export const isCooldownDone = async ( + userData: UsersOnBreak +): Promise => { + const twentyFourHours = 24 * 60 * 60 * 1000; + const userCoolDownTime = userData.addedAt; + return Date.now() - Number(userCoolDownTime) > twentyFourHours; +}; + +export const removeIgnore = (channel: DMChannel) => { + const index = state.ignoredGroupDMs.indexOf(channel.id); + if (index > -1) { + state.ignoredGroupDMs.splice(index, 1); + } +}; + +export const emojiCollector = async ( + optionsMessage: Message, + emoji?: string +) => { + const filter: CollectorFilter<[MessageReaction, User]> = (reaction, user) => + allCollectedEmojis.includes(reaction.emoji.name) && !user.bot; + + const reactions = await optionsMessage.awaitReactions({ + filter, + time: 60000, + max: 1, + }); + if (reactions.size === 0) throw "No reactions"; + + if (!emoji) return reactions.first(); + return reactions.find((e) => e.emoji.toString() === emoji); +}; diff --git a/src/programs/menu/dm-menu.ts b/src/programs/menu/dm-menu.ts index f111b21fe..26c907ad8 100644 --- a/src/programs/menu/dm-menu.ts +++ b/src/programs/menu/dm-menu.ts @@ -1,10 +1,5 @@ -import { - CollectorFilter, - DMChannel, - Message, - MessageReaction, - User, -} from "discord.js"; +import { DMChannel, Message } from "discord.js"; +import { getMember } from "../../common/moderator"; import state from "../../common/state"; import { Command, @@ -12,19 +7,15 @@ import { DiscordEvent, EventLocation, } from "../../event-distribution"; -import { getMember, textLog } from "../../common/moderator"; -import { createYesBotLogger } from "../../log"; - -const logger = createYesBotLogger("programs", "DmMenu"); - -const removeIgnore = (channel: DMChannel) => { - const index = state.ignoredGroupDMs.indexOf(channel.id); - if (index > -1) { - state.ignoredGroupDMs.splice(index, 1); - } -}; - -const availableEmojiOptions = ["👶", "🦥"]; +import { addBreakRole } from "./break-handler/addBreak"; +import { removeBreakRole } from "./break-handler/removeBreak"; +import { + emojiCollector, + handleError, + isOnBreak, + mainOptionsEmojis, +} from "./common"; +import { nameCollector } from "./nameChange"; @Command({ event: DiscordEvent.MESSAGE, @@ -44,13 +35,20 @@ class ShowMenu implements CommandHandler { return; } + const userOnBreak = await isOnBreak(member.id); + + if (userOnBreak) { + await removeBreakRole(member, userOnBreak, dmChannel); + return; + } + if (state.ignoredGroupDMs.includes(dmChannel.id)) return; const optionsMessage = await message.reply( "Hey, I'm just a bot! Most of what I can do, I do on the YesFam discord, so talk to me there instead! I can help you change your name, though, if you're new around here. Click the :baby: if you want to change your name! Or if you feel like you need a break you can click on the :sloth:" ); - availableEmojiOptions.forEach( + mainOptionsEmojis.forEach( async (emoji) => await optionsMessage.react(emoji) ); @@ -60,6 +58,9 @@ class ShowMenu implements CommandHandler { switch (reactions.emoji.toString()) { case "👶": await nameCollector(dmChannel, message); + break; + case "🦥": + await addBreakRole(member, dmChannel); } } catch (err) { await handleError(optionsMessage, dmChannel); @@ -68,91 +69,3 @@ class ShowMenu implements CommandHandler { await optionsMessage.delete(); } } - -const proposeNameChange = async (name: string, botMessage: Message) => { - await botMessage.reply( - "Perfect! I've sent your name request to the mods, hopefully they answer soon! In the meantime, you're free to roam around the server and explore. Maybe post an introduction to get started? :grin:" - ); - const message = `Username: ${botMessage.author.toString()} would like to rename to "${name}". Allow?`; - try { - const sentMessage = await textLog(message); - sentMessage.react("✅").then(() => sentMessage.react("🚫")); - sentMessage - .awaitReactions({ - filter: (_, user: User) => { - return !user.bot; - }, - max: 1, - time: 6000000, - errors: ["time"], - }) - .then((collected) => { - const reaction = collected.first(); - switch (reaction.emoji.toString()) { - case "✅": - const member = getMember(botMessage.author.id); - member.setNickname(name); - sentMessage.delete(); - textLog(`${botMessage.author.toString()} was renamed to ${name}.`); - break; - case "🚫": - sentMessage.delete(); - textLog( - `${botMessage.author.toString()} was *not* renamed to ${name}.` - ); - break; - - default: - break; - } - }); - } catch (err) { - logger.error("(proposeNameChange) Error changing name: ", err); - } -}; - -const nameCollector = async (dmChannel: DMChannel, message: Message) => { - const requestMessage = await dmChannel.send( - "Okay, what's your name then? Please only respond with your name like Henry or Julie, that makes things easier for the Supports! :upside_down:" - ); - state.ignoredGroupDMs.push(dmChannel.id); - const nameMessage = await dmChannel.awaitMessages({ - filter: (message) => !message.author.bot, - time: 60000, - max: 1, - }); - removeIgnore(dmChannel); - - if (nameMessage.size === 0) { - requestMessage.delete(); - throw "No response"; - } - - const requestedName = nameMessage.first().content; - proposeNameChange(requestedName, message); - await requestMessage.delete(); -}; - -const emojiCollector = async (optionsMessage: Message) => { - const filter: CollectorFilter<[MessageReaction, User]> = (reaction, user) => - availableEmojiOptions.includes(reaction.emoji.name) && !user.bot; - - const reactions = await optionsMessage.awaitReactions({ - filter, - time: 60000, - max: 1, - }); - if (reactions.size === 0) throw "No reactions"; - - return reactions.first(); -}; - -const handleError = async (optionsMessage: Message, dmChannel: DMChannel) => { - removeIgnore(dmChannel); - - dmChannel.send( - "Because of technical reasons I can only wait 60 seconds for a reaction. I removed the other message to not confuse you. If you need anything from me, just drop me a message!" - ); - - await optionsMessage.delete(); -}; diff --git a/src/programs/menu/nameChange.ts b/src/programs/menu/nameChange.ts new file mode 100644 index 000000000..f75d8df0b --- /dev/null +++ b/src/programs/menu/nameChange.ts @@ -0,0 +1,59 @@ +import { Message, DMChannel } from "discord.js"; +import { textLog, getMember } from "../../common/moderator"; +import state from "../../common/state"; +import { emojiCollector, logger, removeIgnore } from "./common"; + +export const proposeNameChange = async (name: string, botMessage: Message) => { + await botMessage.reply( + "Perfect! I've sent your name request to the mods, hopefully they answer soon! In the meantime, you're free to roam around the server and explore. Maybe post an introduction to get started? :grin:" + ); + const message = `Username: ${botMessage.author.toString()} would like to rename to "${name}". Allow?`; + try { + const sentMessage = await textLog(message); + await sentMessage.react("✅").then(() => sentMessage.react("🚫")); + + const reaction = await emojiCollector(sentMessage); + + switch (reaction.emoji.toString()) { + case "✅": + const member = getMember(botMessage.author.id); + member.setNickname(name); + sentMessage.delete(); + textLog(`${botMessage.author.toString()} was renamed to ${name}.`); + break; + case "🚫": + sentMessage.delete(); + textLog( + `${botMessage.author.toString()} was *not* renamed to ${name}.` + ); + break; + + default: + break; + } + } catch (err) { + logger.error("(proposeNameChange) Error changing name: ", err); + } +}; + +export const nameCollector = async (dmChannel: DMChannel, message: Message) => { + const requestMessage = await dmChannel.send( + "Okay, what's your name then? Please only respond with your name like Henry or Julie, that makes things easier for the Supports! :upside_down:" + ); + state.ignoredGroupDMs.push(dmChannel.id); + const nameMessage = await dmChannel.awaitMessages({ + filter: (message) => !message.author.bot, + time: 60000, + max: 1, + }); + removeIgnore(dmChannel); + + if (nameMessage.size === 0) { + requestMessage.delete(); + throw "No response"; + } + + const requestedName = nameMessage.first().content; + proposeNameChange(requestedName, message); + await requestMessage.delete(); +};