import { parseYaml } from "../util"; import { Command, ICommandData, Plugin } from "src/model/plugin"; import { tryDo } from "@shared/common/async_utils"; import * as t from "io-ts"; import { logger } from "src/logging"; import { getRepository } from "typeorm"; import { GiveRoleMessage } from "@shared/db/entity/GiveRoleMessage"; import { Message, MessageReaction, ReactionCollector, ReactionEmoji, TextChannel, User } from "discord.js"; import { client } from "src/client"; const ReactRoleMessageParams = t.type({ message: t.string, roleId: t.string }); const REACT_EMOTE = "⭐"; const MSG_COLOR = 9830318; @Plugin export class GiveRoleForReact { @Command({ type: "mention", pattern: "add role message", documentation: { description: "Add role giver message", example: "add role message { message: \"This is a role!\", roleId: \"0237894783782\" }" }, allowDM: false, auth: true }) async makeRoleMessage({ message, contents }: ICommandData): Promise { const textContent = (contents as string).substring("add role message".length).trim(); tryDo(message.delete()); const opts = parseYaml(ReactRoleMessageParams, textContent); if (!opts.ok) { await this.sendDM(message.author, `Sorry, I don't understand the command! Got the following errors: ${opts.errors.join("\n")}`); return; } const params = opts.result; if (!message.guild?.roles.cache.has(params.roleId)) { await this.sendDM(message.author, "Sorry, the role ID is not a valid role on the server!"); return; } const msgSendResult = await tryDo(message.channel.send({ embed: { title: `React with ${REACT_EMOTE} to gain role`, description: params.message, color: MSG_COLOR } })); if (!msgSendResult.ok) { logger.error(`GiveRoleForReact: failed to create message because ${msgSendResult.error}`); return; } const msg = msgSendResult.result; await msg.react(REACT_EMOTE); const roleGiveMessageRepo = getRepository(GiveRoleMessage); if (!msg.guild) { logger.error("GiveRoleForReact: tried to set up role react for DMs (this should never happen!)"); return; } await roleGiveMessageRepo.save({ messageId: msg.id, guildId: msg.guild.id, channelId: msg.channel.id, roleToGive: params.roleId }); this.initReactCollector(msg, params.roleId); } private initReactCollector(msg: Message, role: string) { const check = (reaction: MessageReaction) => { return reaction.emoji.name == REACT_EMOTE; }; const collector = new ReactionCollector(msg, check, { dispose: true }); const guild = msg.guild; if (!guild) { throw new Error("Tried to initialize role collector for non-guild channel."); } collector.on("collect", async (_, user) => { if (user.bot) { return; } const gu = guild.member(user); if (!gu) { return; } const result = await tryDo(gu.roles.add(role)); if (!result.ok) { logger.error("GiveRoleForReact: Can't add role %s to user %s: %s", role, gu.id, result.error); } }); collector.on("remove", async (_, user) => { if (user.bot) { return; } const gu = guild.member(user); if (!gu) { return; } const result = await tryDo(gu.roles.remove(role)); if (!result.ok) { logger.error("GiveRoleForReact: Can't remove role %s to user %s: %s", role, gu.id, result.error); } }); } private async sendDM(usr: User, messageText: string) { const dmResult = await tryDo(usr.createDM()); if (dmResult.ok) { tryDo(dmResult.result.send(messageText)); } } async start(): Promise { logger.info("Initializing role give messages"); const roleGiveMessageRepo = getRepository(GiveRoleMessage); const reactMessages = await roleGiveMessageRepo.find(); const staleEntities = []; for (const reactMessage of reactMessages) { const guildResult = await tryDo(client.bot.guilds.fetch(reactMessage.guildId)); if (!guildResult.ok) { staleEntities.push(reactMessage); continue; } const guild = guildResult.result; const channel = guild.channels.resolve(reactMessage.channelId); if (!channel || !(channel instanceof TextChannel)) { staleEntities.push(reactMessage); continue; } const msgResult = await tryDo(channel.messages.fetch(reactMessage.messageId)); if (!msgResult.ok) { staleEntities.push(reactMessage); continue; } this.initReactCollector(msgResult.result, reactMessage.roleToGive); } await roleGiveMessageRepo.remove(staleEntities); } }