// Copyright (C) 2021-present sam (starshines.gay) // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as published // by the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . using Catalogger.Backend.Extensions; using Microsoft.Extensions.Options; using Remora.Commands.Services; using Remora.Commands.Tokenization; using Remora.Commands.Trees; using Remora.Discord.API.Abstractions.Gateway.Events; using Remora.Discord.API.Abstractions.Objects; using Remora.Discord.API.Abstractions.Rest; using Remora.Discord.API.Objects; using Remora.Discord.Commands.Responders; using Remora.Discord.Commands.Services; using Remora.Discord.Gateway.Responders; using Remora.Rest.Core; using Remora.Results; using Serilog.Context; namespace Catalogger.Backend.Bot.Responders; /// /// Wrapper for Remora.Discord's default interaction responder, that ignores all events if test mode is enabled, /// and handles results returned by commands. /// public class CustomInteractionResponder( Config config, ILogger logger, CommandService commandService, IOptions options, IDiscordRestInteractionAPI interactionAPI, ExecutionEventCollectorService eventCollector, IServiceProvider services, ContextInjectionService contextInjection, IOptions tokenizerOptions, IOptions treeSearchOptions, ITreeNameResolver? treeNameResolver = null ) : IResponder { private readonly ILogger _logger = logger.ForContext(); private readonly InteractionResponder _inner = new( commandService, options, interactionAPI, eventCollector, services, contextInjection, tokenizerOptions, treeSearchOptions, treeNameResolver ); public async Task RespondAsync(IInteractionCreate evt, CancellationToken ct = default) { if (config.Discord.TestMode) { _logger.Information( "Not responding to interaction create event {InteractionId} in {ChannelId} as test mode is enabled", evt.ID, evt.Channel.Map(c => c.ID).OrDefault() ); return Result.Success; } using var _ = LogUtils.PushProperties( ("Event", nameof(IInteractionCreate)), ("InteractionId", evt.ID), ("GuildId", evt.GuildID), ("UserId", evt.User.Map(u => u.ID)), ("MemberId", evt.Member.Map(m => m.User.Map(u => u.ID).OrDefault())), ("ChannelId", evt.Channel.Map(c => c.ID)), ("InteractionType", evt.Type) ); using var __ = LogContext.PushProperty( "InteractionData", evt.Data.HasValue ? (object?)evt.Data.Value : null, true ); var result = await _inner.RespondAsync(evt, ct); if (result.Error is not CataloggerError cataloggerError) return result; return await interactionAPI.CreateInteractionResponseAsync( evt.ID, evt.Token, new InteractionResponse( Type: InteractionCallbackType.ChannelMessageWithSource, Data: new Optional>( new InteractionMessageCallbackData( Embeds: new Optional>( [ new Embed( Colour: DiscordUtils.Red, Title: "Something went wrong", Description: $""" Something went wrong while running this command. > {cataloggerError.Message} Please try again later. """ ), ] ) ) ) ), ct: ct ); } }