diff --git a/config/default.yml b/config/default.yml index 27241077..01d68c5e 100644 --- a/config/default.yml +++ b/config/default.yml @@ -28,7 +28,8 @@ projects: request: invalidTicketEmoji: ⏸ noLinkEmoji: ⛔ - warningLifetime: 5000 + duplicateRequestEmoji: 🔁 + warningLifetime: 7500 waitingEmoji: ⌛ invalidRequestJql: created > -24h diff --git a/src/BotConfig.ts b/src/BotConfig.ts index 9f64abed..e67d0fef 100644 --- a/src/BotConfig.ts +++ b/src/BotConfig.ts @@ -23,6 +23,7 @@ export class RequestConfig { public invalidTicketEmoji: string; public noLinkEmoji: string; + public duplicateRequestEmoji: string; public warningLifetime: number; public invalidRequestJql: string; public waitingEmoji: string; @@ -48,6 +49,7 @@ export class RequestConfig { this.invalidTicketEmoji = config.get( 'request.invalidTicketEmoji' ); this.noLinkEmoji = config.get( 'request.noLinkEmoji' ); + this.duplicateRequestEmoji = config.get( 'request.duplicateRequestEmoji' ); this.warningLifetime = config.get( 'request.warningLifetime' ); this.invalidRequestJql = config.get( 'request.invalidRequestJql' ); this.waitingEmoji = config.get( 'request.waitingEmoji' ); diff --git a/src/events/request/RequestEventHandler.ts b/src/events/request/RequestEventHandler.ts index 0600db35..8c27680e 100644 --- a/src/events/request/RequestEventHandler.ts +++ b/src/events/request/RequestEventHandler.ts @@ -62,7 +62,7 @@ export default class RequestEventHandler implements EventHandler<'message'> { return; } - if ( BotConfig.request.invalidRequestJql ) { + if ( BotConfig.request.invalidTicketEmoji && BotConfig.request.invalidRequestJql ) { if ( !await RequestsUtil.checkTicketValidity( tickets ) ) { try { await origin.react( BotConfig.request.invalidTicketEmoji ); @@ -84,6 +84,28 @@ export default class RequestEventHandler implements EventHandler<'message'> { const internalChannelId = this.internalChannels.get( origin.channel.id ); const internalChannel = await DiscordUtil.getChannel( internalChannelId ); + if ( BotConfig.request.duplicateRequestEmoji && internalChannel instanceof TextChannel ) { + const match = RequestsUtil.findExactMatchInPendingRequests( origin, internalChannel ); + + if ( match != origin ) { + const parent = await RequestsUtil.getOriginMessage( match ); + + try { + await origin.react( BotConfig.request.duplicateRequestEmoji ); + } catch ( error ) { + this.logger.error( error ); + } + + try { + const warning = await origin.channel.send( `${ origin.author }, your request (<${ origin.url }>) has already been requested at <${ parent.url }>.` ); + await DiscordUtil.deleteWithDelay( warning, BotConfig.request.warningLifetime ); + } catch ( error ) { + this.logger.error( error ); + } + return; + } + } + if ( requestLimit && requestLimit >= 0 && internalChannel instanceof TextChannel ) { const internalChannelUserMessages = internalChannel.messages.cache .filter( message => message.embeds.length > 0 && message.embeds[0].author.name == origin.author.tag ) diff --git a/src/util/RequestsUtil.ts b/src/util/RequestsUtil.ts index 8ef3d772..8c02ec1a 100644 --- a/src/util/RequestsUtil.ts +++ b/src/util/RequestsUtil.ts @@ -1,4 +1,4 @@ -import { EmbedField, Message, PartialMessage, Snowflake, TextChannel, User } from 'discord.js'; +import { EmbedField, Message, PartialMessage, Snowflake, TextChannel, User, TextBasedChannels } from 'discord.js'; import * as log4js from 'log4js'; import BotConfig from '../BotConfig'; import DiscordUtil from './DiscordUtil'; @@ -145,4 +145,14 @@ export class RequestsUtil { return content.replace( /([[\]\\])/gm, '\\$1' ) .replace( regex, '[$$](https://bugs.mojang.com/browse/$$$)' ); } + + public static findExactMatchInPendingRequests( origin: Message, internalChannel: TextBasedChannels ): Message { + const matcher = this.replaceTicketReferencesWithRichLinks( origin.content ); + if ( internalChannel instanceof TextChannel ) { + const matches = internalChannel.messages.cache.filter( message => message.embeds.length > 0 && message.embeds[0].description == matcher ); + return matches.size > 0 ? matches.first() : origin; + } else { + return origin; + } + } }