diff --git a/src/MojiraBot.ts b/src/MojiraBot.ts index 76870e95..08c35089 100644 --- a/src/MojiraBot.ts +++ b/src/MojiraBot.ts @@ -91,7 +91,9 @@ export default class MojiraBot { this.botUser = this.client.user; this.running = true; - this.logger.info( `MojiraBot has been started successfully. Logged in as ${ this.botUser.tag }` ); + this.logger.info( + `MojiraBot has been started successfully. Logged in as ${ DiscordUtil.getUserHandle( this.botUser ) }` + ); // Register events. EventRegistry.setClient( this.client ); diff --git a/src/commands/BugCommand.ts b/src/commands/BugCommand.ts index bc27a96f..1880763c 100644 --- a/src/commands/BugCommand.ts +++ b/src/commands/BugCommand.ts @@ -4,6 +4,7 @@ import { MentionRegistry } from '../mentions/MentionRegistry.js'; import BotConfig from '../BotConfig.js'; import { ChannelConfigUtil } from '../util/ChannelConfigUtil.js'; import SlashCommand from './commandHandlers/SlashCommand.js'; +import DiscordUtil from '../util/DiscordUtil.js'; export default class BugCommand extends SlashCommand { public slashCommandBuilder = this.slashCommandBuilder @@ -55,7 +56,7 @@ export default class BugCommand extends SlashCommand { if ( embed === undefined ) return false; - embed.setFooter( { text: interaction.user.tag, iconURL: interaction.user.avatarURL() ?? undefined } ) + embed.setFooter( DiscordUtil.getUserFooter( interaction.user ) ) .setTimestamp( interaction.createdAt ); try { diff --git a/src/commands/HelpCommand.ts b/src/commands/HelpCommand.ts index f06140f5..f858895f 100644 --- a/src/commands/HelpCommand.ts +++ b/src/commands/HelpCommand.ts @@ -1,6 +1,7 @@ import { ChatInputCommandInteraction, EmbedBuilder } from 'discord.js'; import BotConfig from '../BotConfig.js'; import SlashCommand from './commandHandlers/SlashCommand.js'; +import DiscordUtil from '../util/DiscordUtil.js'; export default class HelpCommand extends SlashCommand { public readonly slashCommandBuilder = this.slashCommandBuilder @@ -30,7 +31,7 @@ export default class HelpCommand extends SlashCommand { \`/tips\` - Sends helpful info on how to use the bug tracker and this Discord server.`, } ) - .setFooter( { text: interaction.user.tag, iconURL: interaction.user.avatarURL() ?? undefined } ); + .setFooter( DiscordUtil.getUserFooter( interaction.user ) ); await interaction.reply( { embeds: [embed], ephemeral: true } ); } catch { return false; diff --git a/src/commands/MentionCommand.ts b/src/commands/MentionCommand.ts index d800bce1..51200cfa 100644 --- a/src/commands/MentionCommand.ts +++ b/src/commands/MentionCommand.ts @@ -71,13 +71,8 @@ export default class MentionCommand extends Command { } if ( embed === undefined ) return false; - let messageAuthorNormalizedTag: string; - if ( message.author.discriminator === '0' ) { - messageAuthorNormalizedTag = message.author.username; - } else { - messageAuthorNormalizedTag = message.author.tag; - } - embed.setFooter( { text: messageAuthorNormalizedTag, iconURL: message.author.avatarURL() ?? undefined } ) + + embed.setFooter( DiscordUtil.getUserFooter( message.author ) ) .setTimestamp( message.createdAt ); try { diff --git a/src/commands/MooCommand.ts b/src/commands/MooCommand.ts index b4cb7fd2..f726ba77 100644 --- a/src/commands/MooCommand.ts +++ b/src/commands/MooCommand.ts @@ -2,6 +2,7 @@ import { ChatInputCommandInteraction, Message } from 'discord.js'; import { SingleMention } from '../mentions/SingleMention.js'; import { ReactionsUtil } from '../util/ReactionsUtil.js'; import SlashCommand from './commandHandlers/SlashCommand.js'; +import DiscordUtil from '../util/DiscordUtil.js'; export default class MooCommand extends SlashCommand { public readonly slashCommandBuilder = this.slashCommandBuilder @@ -13,7 +14,7 @@ export default class MooCommand extends SlashCommand { if ( interaction.channel === null ) return false; const mention = new SingleMention( 'MC-772', interaction.channel ); const embed = await mention.getEmbed(); - embed.setFooter( { text: interaction.user.tag, iconURL: interaction.user.avatarURL() ?? undefined } ); + embed.setFooter( DiscordUtil.getUserFooter( interaction.user ) ); const message = await interaction.reply( { embeds: [embed], fetchReply: true } ); if ( message instanceof Message ) { await ReactionsUtil.reactToMessage( message, ['🐮', '🐄', '🥛'] ); diff --git a/src/commands/PollCommand.ts b/src/commands/PollCommand.ts index 6a7c8d9e..fd06fcda 100644 --- a/src/commands/PollCommand.ts +++ b/src/commands/PollCommand.ts @@ -4,6 +4,7 @@ import emojiRegex from 'emoji-regex'; import PermissionRegistry from '../permissions/PermissionRegistry.js'; import { ReactionsUtil } from '../util/ReactionsUtil.js'; import SlashCommand from './commandHandlers/SlashCommand.js'; +import DiscordUtil from '../util/DiscordUtil.js'; interface PollOption { emoji: string; @@ -53,7 +54,7 @@ export default class PollCommand extends SlashCommand { private async sendPollMessage( interaction: ChatInputCommandInteraction, title: string, options: PollOption[] ): Promise { const embed = new EmbedBuilder(); embed.setTitle( 'Poll' ) - .setFooter( { text: interaction.user.tag, iconURL: interaction.user.avatarURL() ?? undefined } ) + .setFooter( DiscordUtil.getUserFooter( interaction.user ) ) .setTimestamp( interaction.createdAt ) .setColor( 'Green' ); diff --git a/src/commands/SearchCommand.ts b/src/commands/SearchCommand.ts index 4f6350b2..b5230bcc 100644 --- a/src/commands/SearchCommand.ts +++ b/src/commands/SearchCommand.ts @@ -3,6 +3,7 @@ import SlashCommand from './commandHandlers/SlashCommand.js'; import BotConfig from '../BotConfig.js'; import MojiraBot from '../MojiraBot.js'; import { ChannelConfigUtil } from '../util/ChannelConfigUtil.js'; +import DiscordUtil from '../util/DiscordUtil.js'; export default class SearchCommand extends SlashCommand { public readonly slashCommandBuilder = this.slashCommandBuilder @@ -35,7 +36,7 @@ export default class SearchCommand extends SlashCommand { } embed.setTitle( '**Results:**' ); - embed.setFooter( { text: interaction.user.tag, iconURL: interaction.user.avatarURL() ?? undefined } ); + embed.setFooter( DiscordUtil.getUserFooter( interaction.user ) ); for ( const issue of searchResults.issues ) { embed.addFields( { diff --git a/src/commands/TipsCommand.ts b/src/commands/TipsCommand.ts index b6ccc1f1..eff52ed2 100644 --- a/src/commands/TipsCommand.ts +++ b/src/commands/TipsCommand.ts @@ -1,5 +1,6 @@ import { ChatInputCommandInteraction, EmbedBuilder } from 'discord.js'; import SlashCommand from './commandHandlers/SlashCommand.js'; +import DiscordUtil from '../util/DiscordUtil.js'; export default class TipsCommand extends SlashCommand { public readonly slashCommandBuilder = this.slashCommandBuilder @@ -17,7 +18,7 @@ export default class TipsCommand extends SlashCommand { Start by choosing which bug tracker projects you would like to be a part of in <#648479533246316555>. Afterwards, you can use corresponding request channels in each project to make requests for changes to tickets on the bug tracker, like resolutions and adding affected versions. The moderators and helpers of the bug tracker will then be able to see the requests and resolve them.`.replace( /\t/g, '' ) ) - .setFooter( { text: interaction.user.tag, iconURL: interaction.user.avatarURL() ?? undefined } ); + .setFooter( DiscordUtil.getUserFooter( interaction.user ) ); await interaction.reply( { embeds: [embed], ephemeral: true } ); } catch { return false; diff --git a/src/commands/commandHandlers/CommandExecutor.ts b/src/commands/commandHandlers/CommandExecutor.ts index e5138b74..1708593d 100644 --- a/src/commands/commandHandlers/CommandExecutor.ts +++ b/src/commands/commandHandlers/CommandExecutor.ts @@ -1,6 +1,7 @@ import Command from './Command.js'; import { Message } from 'discord.js'; import DefaultCommandRegistry from './DefaultCommandRegistry.js'; +import DiscordUtil from '../../util/DiscordUtil.js'; export default class CommandExecutor { public static async checkCommands( message: Message ): Promise { @@ -14,7 +15,9 @@ export default class CommandExecutor { const args = commandTestResult === true ? '' : commandTestResult; - Command.logger.info( `User ${ message.author.tag } ran command ${ command.asString( args ) }` ); + Command.logger.info( + `User ${ DiscordUtil.getUserHandle( message.author ) } ran command ${ command.asString( args ) }` + ); return await command.run( message, args ); } } diff --git a/src/commands/commandHandlers/SlashCommandRegister.ts b/src/commands/commandHandlers/SlashCommandRegister.ts index eb3b0298..e4c5c700 100644 --- a/src/commands/commandHandlers/SlashCommandRegister.ts +++ b/src/commands/commandHandlers/SlashCommandRegister.ts @@ -5,6 +5,7 @@ import { Routes } from 'discord-api-types/v9'; import { Client, Collection, RESTPostAPIApplicationCommandsJSONBody, ChatInputCommandInteraction, GuildMember } from 'discord.js'; import { SlashCommandJsonData } from '../../types/discord.js'; import { ChannelConfigUtil } from '../../util/ChannelConfigUtil.js'; +import DiscordUtil from '../../util/DiscordUtil.js'; export default class SlashCommandRegister { public static async registerCommands( client: Client, token: string ) { @@ -23,7 +24,9 @@ export default class SlashCommandRegister { const jsonData: SlashCommandJsonData = { data: command.slashCommandBuilder, async execute( interaction: ChatInputCommandInteraction ) { - SlashCommand.logger.info( `User ${ interaction.user.tag } ran command ${ command.asString( interaction ) }` ); + SlashCommand.logger.info( + `User ${ DiscordUtil.getUserHandle( interaction.user ) } ran command ${ command.asString( interaction ) }` + ); const member = interaction.member instanceof GuildMember ? interaction.member : await fetchedGuild.members.fetch( interaction.user ); diff --git a/src/events/mention/MentionDeleteEventHandler.ts b/src/events/mention/MentionDeleteEventHandler.ts index ee709fd5..f0042714 100644 --- a/src/events/mention/MentionDeleteEventHandler.ts +++ b/src/events/mention/MentionDeleteEventHandler.ts @@ -1,26 +1,42 @@ -import { MessageReaction, User } from 'discord.js'; +import { MessageReaction, Snowflake, User } from 'discord.js'; import log4js from 'log4js'; import EventHandler from '../EventHandler.js'; +import DiscordUtil from '../../util/DiscordUtil.js'; export default class MentionDeleteEventHandler implements EventHandler<'messageReactionAdd'> { public readonly eventName = 'messageReactionAdd'; private logger = log4js.getLogger( 'MentionDeleteEventHandler' ); - public onEvent = async ( { message }: MessageReaction, user: User ): Promise => { - this.logger.info( `User ${ user.tag } is attempting to delete message '${ message.id }'` ); + private botUserId: Snowflake; + constructor( botUserId: Snowflake ) { + this.botUserId = botUserId; + } + public onEvent = async ( reaction: MessageReaction, user: User ): Promise => { + const message = reaction.message; + + this.logger.info( + `User ${ DiscordUtil.getUserHandle( user ) } is attempting to delete message '${ message.id }'` + ); + + // Only delete own messages + if ( message.author?.id !== this.botUserId ) return; + + // Check whether the footer of the message's embed matches the user's handle const footer = message.embeds[0]?.footer?.text; if ( footer === undefined ) return; - const userTag = footer.match( /.{3,32}#[0-9]{4}/ ); - - if ( userTag !== null && user.tag === userTag[0] ) { + if ( DiscordUtil.getUserHandle( user ) === footer ) { try { + this.logger.info( `Removing message '${ message.id }'` ); await message.delete(); } catch ( error ) { this.logger.error( error ); } + } else { + this.logger.info( `Message '${ message.id }' was not removed; no permission` ); + await reaction.remove(); } }; } diff --git a/src/events/modmail/ModmailEventHandler.ts b/src/events/modmail/ModmailEventHandler.ts index 00a8535d..45599724 100644 --- a/src/events/modmail/ModmailEventHandler.ts +++ b/src/events/modmail/ModmailEventHandler.ts @@ -52,7 +52,7 @@ export default class ModmailEventHandler implements EventHandler<'messageCreate' await origin.react( '📬' ); const newThread = await start.startThread( { - name: origin.author.tag, + name: DiscordUtil.getUserHandle( origin.author ), autoArchiveDuration: 1440, } ); diff --git a/src/events/reaction/ReactionAddEventHandler.ts b/src/events/reaction/ReactionAddEventHandler.ts index e2f6b6e6..a6b9ea66 100644 --- a/src/events/reaction/ReactionAddEventHandler.ts +++ b/src/events/reaction/ReactionAddEventHandler.ts @@ -18,7 +18,7 @@ export default class ReactionAddEventHandler implements DiscordEventHandler<'mes private readonly requestResolveEventHandler: RequestResolveEventHandler; private readonly requestReactionRemovalEventHandler = new RequestReactionRemovalEventHandler(); private readonly requestReopenEventHandler: RequestReopenEventHandler; - private readonly mentionDeleteEventHandler = new MentionDeleteEventHandler(); + private readonly mentionDeleteEventHandler: MentionDeleteEventHandler; constructor( botUserId: Snowflake, internalChannels: Map, requestLimits: Map ) { this.botUserId = botUserId; @@ -26,6 +26,7 @@ export default class ReactionAddEventHandler implements DiscordEventHandler<'mes const requestEventHandler = new RequestEventHandler( internalChannels, requestLimits ); this.requestResolveEventHandler = new RequestResolveEventHandler( botUserId ); this.requestReopenEventHandler = new RequestReopenEventHandler( botUserId, requestEventHandler ); + this.mentionDeleteEventHandler = new MentionDeleteEventHandler( botUserId ); } // This syntax is used to ensure that `this` refers to the `ReactionAddEventHandler` object diff --git a/src/events/request/RequestDeleteEventHandler.ts b/src/events/request/RequestDeleteEventHandler.ts index 2eaefa4f..0cef311c 100644 --- a/src/events/request/RequestDeleteEventHandler.ts +++ b/src/events/request/RequestDeleteEventHandler.ts @@ -21,7 +21,9 @@ export default class RequestDeleteEventHandler implements EventHandler<'messageD // This syntax is used to ensure that `this` refers to the `RequestDeleteEventHandler` object public onEvent = async ( origin: Message ): Promise => { - this.logger.info( `User ${ origin.author.tag }'s request ${ origin.id } in channel ${ origin.channel.id } was deleted` ); + this.logger.info( + `User ${ DiscordUtil.getUserHandle( origin.author ) }'s request ${ origin.id } in channel ${ origin.channel.id } was deleted` + ); const internalChannelId = this.internalChannels.get( origin.channel.id ); if ( internalChannelId === undefined ) return; diff --git a/src/events/request/RequestEventHandler.ts b/src/events/request/RequestEventHandler.ts index 250aefed..73ee8767 100644 --- a/src/events/request/RequestEventHandler.ts +++ b/src/events/request/RequestEventHandler.ts @@ -34,7 +34,9 @@ export default class RequestEventHandler implements EventHandler<'messageCreate' } if ( origin.channel instanceof TextChannel ) { - this.logger.info( `${ origin.author.tag } posted request ${ origin.id } in #${ origin.channel.name }` ); + this.logger.info( + `${ DiscordUtil.getUserHandle( origin.author ) } posted request ${ origin.id } in #${ origin.channel.name }` + ); } try { @@ -89,7 +91,7 @@ export default class RequestEventHandler implements EventHandler<'messageCreate' if ( !forced && requestLimit && requestLimit >= 0 && internalChannel instanceof TextChannel ) { // Check for 24 hour rolling window request limit const internalChannelUserMessages = internalChannel.messages.cache - .filter( message => message.embeds.length > 0 && message.embeds[0].author?.name === origin.author.tag ) + .filter( message => message.embeds.length > 0 && message.embeds[0].author?.name === DiscordUtil.getUserHandle( origin.author ) ) .filter( message => { // Check if message is at most 24 hours old if ( message.embeds[0].timestamp === null ) return false; @@ -124,7 +126,7 @@ export default class RequestEventHandler implements EventHandler<'messageCreate' if ( internalChannel && internalChannel instanceof TextChannel ) { const embed = new EmbedBuilder() .setColor( RequestsUtil.getEmbedColor() ) - .setAuthor( { name: origin.author.tag, iconURL: origin.author.avatarURL() ?? undefined } ) + .setAuthor( DiscordUtil.getUserAsEmbedAuthor( origin.author ) ) .setDescription( RequestsUtil.getRequestDescription( origin ) ) .addFields( { name: 'Go To', diff --git a/src/events/request/RequestReactionRemovalEventHandler.ts b/src/events/request/RequestReactionRemovalEventHandler.ts index cd4cdd6a..523583d0 100644 --- a/src/events/request/RequestReactionRemovalEventHandler.ts +++ b/src/events/request/RequestReactionRemovalEventHandler.ts @@ -12,7 +12,9 @@ export default class RequestReactionRemovalEventHandler implements EventHandler< public onEvent = async ( reaction: MessageReaction, user: User ): Promise => { const message = await DiscordUtil.fetchMessage( reaction.message ); - this.logger.info( `User ${ user.tag } added '${ reaction.emoji.name }' reaction to request message '${ message.id }'` ); + this.logger.info( + `User ${ DiscordUtil.getUserHandle( user ) } added '${ reaction.emoji.name }' reaction to request message '${ message.id }'` + ); const guild = message.guild; if ( guild === null ) return; diff --git a/src/events/request/RequestReopenEventHandler.ts b/src/events/request/RequestReopenEventHandler.ts index cbc7be62..a8b38ab6 100644 --- a/src/events/request/RequestReopenEventHandler.ts +++ b/src/events/request/RequestReopenEventHandler.ts @@ -21,7 +21,9 @@ export default class RequestReopenEventHandler implements EventHandler<'messageR // This syntax is used to ensure that `this` refers to the `RequestReopenEventHandler` object public onEvent = async ( { message }: MessageReaction, user: User ): Promise => { - this.logger.info( `User ${ user.tag } is reopening the request message '${ message.id }'` ); + this.logger.info( + `User ${ DiscordUtil.getUserHandle( user ) } is reopening the request message '${ message.id }'` + ); message = await DiscordUtil.fetchMessage( message ); @@ -40,14 +42,14 @@ export default class RequestReopenEventHandler implements EventHandler<'messageR if ( logChannel && logChannel instanceof TextChannel ) { const log = new EmbedBuilder() .setColor( 'Orange' ) - .setAuthor( { name: requestMessage.author.tag, iconURL: requestMessage.author.avatarURL() ?? undefined } ) + .setAuthor( DiscordUtil.getUserAsEmbedAuthor( requestMessage.author ) ) .setDescription( requestMessage.content ) .addFields( { name: 'Message', value: `[Here](${ requestMessage.url })`, inline: true }, { name: 'Channel', value: requestMessage.channel.toString(), inline: true }, { name: 'Created', value: requestMessage.createdAt.toUTCString(), inline: false } ) - .setFooter( { text: `${ user.tag } reopened this request`, iconURL: user.avatarURL() ?? undefined } ) + .setFooter( DiscordUtil.getUserFooter( user, ' reopened this request' ) ) .setTimestamp( new Date() ); try { diff --git a/src/events/request/RequestResolveEventHandler.ts b/src/events/request/RequestResolveEventHandler.ts index c13e1b8b..e1ebc0ff 100644 --- a/src/events/request/RequestResolveEventHandler.ts +++ b/src/events/request/RequestResolveEventHandler.ts @@ -5,6 +5,7 @@ import ResolveRequestMessageTask from '../../tasks/ResolveRequestMessageTask.js' import TaskScheduler from '../../tasks/TaskScheduler.js'; import { RequestsUtil } from '../../util/RequestsUtil.js'; import EventHandler from '../EventHandler.js'; +import DiscordUtil from '../../util/DiscordUtil.js'; export default class RequestResolveEventHandler implements EventHandler<'messageReactionAdd'> { public readonly eventName = 'messageReactionAdd'; @@ -21,7 +22,9 @@ export default class RequestResolveEventHandler implements EventHandler<'message public onEvent = async ( reaction: MessageReaction, user: User ): Promise => { if ( reaction.message?.author?.id !== this.botUserId ) return; - this.logger.info( `User ${ user.tag } added '${ reaction.emoji.name }' reaction to request message '${ reaction.message.id }'` ); + this.logger.info( + `User ${ DiscordUtil.getUserHandle( user ) } added '${ reaction.emoji.name }' reaction to request message '${ reaction.message.id }'` + ); const embed = new EmbedBuilder( reaction.message.embeds[0].data ).setColor( RequestsUtil.getEmbedColor( user ) ); await reaction.message.edit( { embeds: [embed] } ); diff --git a/src/events/request/RequestUnresolveEventHandler.ts b/src/events/request/RequestUnresolveEventHandler.ts index a10519b7..0b974d8b 100644 --- a/src/events/request/RequestUnresolveEventHandler.ts +++ b/src/events/request/RequestUnresolveEventHandler.ts @@ -22,11 +22,15 @@ export default class RequestUnresolveEventHandler implements EventHandler<'messa message = await DiscordUtil.fetchMessage( message ); if ( message.author.id !== this.botUserId ) { - this.logger.info( `User ${ user.tag } removed '${ emoji.name }' reaction from non-bot message '${ message.id }'. Ignored` ); + this.logger.info( + `User ${ DiscordUtil.getUserHandle( user ) } removed '${ emoji.name }' reaction from non-bot message '${ message.id }'. Ignored` + ); return; } - this.logger.info( `User ${ user.tag } removed '${ emoji.name }' reaction from request message '${ message.id }'` ); + this.logger.info( + `User ${ DiscordUtil.getUserHandle( user ) } removed '${ emoji.name }' reaction from request message '${ message.id }'` + ); const embed = new EmbedBuilder( message.embeds[0].data ).setColor( RequestsUtil.getEmbedColor() ); diff --git a/src/events/request/RequestUpdateEventHandler.ts b/src/events/request/RequestUpdateEventHandler.ts index 1ecc843f..89c26074 100644 --- a/src/events/request/RequestUpdateEventHandler.ts +++ b/src/events/request/RequestUpdateEventHandler.ts @@ -20,7 +20,9 @@ export default class RequestUpdateEventHandler implements EventHandler<'messageU // This syntax is used to ensure that `this` refers to the `RequestUpdateEventHandler` object public onEvent = async ( oldMessage: Message, newMessage: Message ): Promise => { - this.logger.info( `User ${ oldMessage.author.tag }'s request ${ oldMessage.id } in channel ${ oldMessage.channel.id } was updated` ); + this.logger.info( + `User ${ DiscordUtil.getUserHandle( oldMessage.author ) }'s request ${ oldMessage.id } in channel ${ oldMessage.channel.id } was updated` + ); const internalChannelId = this.internalChannels.get( oldMessage.channel.id ); if ( internalChannelId === undefined ) return; @@ -36,7 +38,7 @@ export default class RequestUpdateEventHandler implements EventHandler<'messageU if ( result.channelId === oldMessage.channel.id && result.messageId === oldMessage.id ) { try { const embed = new EmbedBuilder( internalMessage.embeds[0].data ); - embed.setAuthor( { name: oldMessage.author.tag, iconURL: oldMessage.author.avatarURL() ?? undefined } ); + embed.setAuthor( DiscordUtil.getUserAsEmbedAuthor( oldMessage.author ) ); embed.setDescription( RequestsUtil.getRequestDescription( newMessage ) ); await internalMessage.edit( { embeds: [embed] } ); } catch ( error ) { diff --git a/src/events/request/TestingRequestEventHandler.ts b/src/events/request/TestingRequestEventHandler.ts index 575b21d0..9b6861c8 100644 --- a/src/events/request/TestingRequestEventHandler.ts +++ b/src/events/request/TestingRequestEventHandler.ts @@ -13,7 +13,9 @@ export default class TestingRequestEventHandler implements EventHandler<'message // This syntax is used to ensure that `this` refers to the `TestingRequestEventHandler` object public onEvent = async ( request: Message ): Promise => { if ( request.channel instanceof TextChannel ) { - this.logger.info( `${ request.author.tag } posted request ${ request.id } in #${ request.channel.name }` ); + this.logger.info( + `${ DiscordUtil.getUserHandle( request.author ) } posted request ${ request.id } in #${ request.channel.name }` + ); } const guildMember = request?.guild?.members?.resolve( request.author ); diff --git a/src/tasks/ResolveRequestMessageTask.ts b/src/tasks/ResolveRequestMessageTask.ts index 2908b57f..3471600f 100644 --- a/src/tasks/ResolveRequestMessageTask.ts +++ b/src/tasks/ResolveRequestMessageTask.ts @@ -49,14 +49,14 @@ export default class ResolveRequestMessageTask extends MessageTask { if ( logChannel && logChannel instanceof TextChannel ) { const log = new EmbedBuilder() .setColor( 'Green' ) - .setAuthor( { name: origin.author.tag, iconURL: origin.author.avatarURL() ?? undefined } ) + .setAuthor( DiscordUtil.getUserAsEmbedAuthor( origin.author ) ) .setDescription( origin.content ) .addFields( { name: 'Message', value: `[Here](${ origin.url })`, inline: true }, { name: 'Channel', value: origin.channel.toString(), inline: true }, { name: 'Created', value: origin.createdAt.toUTCString(), inline: false }, ) - .setFooter( { text: `${ this.user.tag } resolved as ${ this.emoji }`, iconURL: this.user.avatarURL() ?? undefined } ) + .setFooter( DiscordUtil.getUserFooter( this.user, ` resolved as ${ this.emoji }` ) ) .setTimestamp( new Date() ); try { diff --git a/src/util/DiscordUtil.ts b/src/util/DiscordUtil.ts index 487313df..afff93c4 100644 --- a/src/util/DiscordUtil.ts +++ b/src/util/DiscordUtil.ts @@ -1,6 +1,6 @@ import log4js from 'log4js'; import MojiraBot from '../MojiraBot.js'; -import { TextChannel, Message, Guild, GuildMember, MessageReaction, User, Snowflake, PartialMessage, TextBasedChannel, ReplyMessageOptions } from 'discord.js'; +import { TextChannel, Message, Guild, GuildMember, MessageReaction, User, Snowflake, PartialMessage, TextBasedChannel, ReplyMessageOptions, EmbedFooterData, EmbedAuthorData } from 'discord.js'; export default class DiscordUtil { private static logger = log4js.getLogger( 'DiscordUtil' ); @@ -70,4 +70,20 @@ export default class DiscordUtil { this.logger.error( e ); } } + + public static getUserHandle( user: User ): string { + if ( user.discriminator == '0' ) { + return '@' + user.username; + } else { + return user.tag; + } + } + + public static getUserFooter( user: User, text = '' ): EmbedFooterData { + return { text: this.getUserHandle( user ) + text, iconURL: user.avatarURL() ?? undefined }; + } + + public static getUserAsEmbedAuthor( user: User ): EmbedAuthorData { + return { name: this.getUserHandle( user ), iconURL: user.avatarURL() ?? undefined }; + } }