Exemplo n.º 1
0
    public async Task <IResult> ExportRoleplayAsync
    (
        [RequireEntityOwner]
        [AutocompleteProvider("roleplay::owned")]
        Roleplay roleplay,
        ExportFormat format = ExportFormat.PDF
    )
    {
        IRoleplayExporter exporter;

        switch (format)
        {
        case ExportFormat.PDF:
        {
            exporter = new PDFRoleplayExporter();
            break;
        }

        case ExportFormat.Plaintext:
        {
            exporter = new PlaintextRoleplayExporter();
            break;
        }

        default:
        {
            return(Result.FromError
                   (
                       new UserError("That export format hasn't been implemented yet.")
                   ));
        }
        }

        var send = await _feedback.SendContextualNeutralAsync("Compiling the roleplay...");

        if (!send.IsSuccess)
        {
            return(Result <FeedbackMessage> .FromError(send));
        }

        using var output = await exporter.ExportAsync(_services, roleplay);

        var fileData = new FileData
                       (
            $"{output.Title}.{output.Format.GetFileExtension()}",
            output.Data
                       );

        return(await _channelAPI.CreateMessageAsync
               (
                   _context.ChannelID,
                   attachments : new List <OneOf <FileData, IPartialAttachment> > {
            fileData
        }
               ));
    }
Exemplo n.º 2
0
        private async Task <Result> ReplyWithFailureAsync(Snowflake channel)
        {
            var failEmbed = new Embed(Description: "Dice rolling failed :(", Colour: Color.OrangeRed);

            var replyFail = await _channelAPI.CreateMessageAsync(channel, embeds : new[] { failEmbed });

            return(!replyFail.IsSuccess
                ? Result.FromError(replyFail)
                : Result.FromSuccess());
        }
Exemplo n.º 3
0
    public async Task <IResult> DisplayContactAsync()
    {
        const string avatarURL = "https://i.imgur.com/2E334jS.jpg";
        var          embed     = new Embed
        {
            Colour    = _feedback.Theme.Secondary,
            Title     = "Privacy Contact",
            Author    = new EmbedAuthor("Jarl Gullberg", IconUrl: avatarURL, Url: "https://github.com/Nihlus/"),
            Thumbnail = new EmbedThumbnail(avatarURL),
            Fields    = new[]
            {
                new EmbedField("Email", "*****@*****.**", true),
                new EmbedField("Discord", "Jax#7487", true)
            },
            Footer = new EmbedFooter
                     (
                "Not your contact person? Edit the source of your instance with the correct information."
                     )
        };

        var sendEmbed = await _channelAPI.CreateMessageAsync
                        (
            _context.ChannelID,
            embeds : new[] { embed },
            ct : this.CancellationToken
                        );

        return(sendEmbed.IsSuccess
            ? Result.FromSuccess()
            : Result.FromError(sendEmbed));
    }
Exemplo n.º 4
0
    /// <summary>
    /// Sends a consent request from the given user.
    /// </summary>
    /// <param name="discordUser">The user to request consent from.</param>
    /// <param name="ct">The cancellation token for this operation.</param>
    /// <returns>An execution result.</returns>
    public async Task <Result> RequestConsentAsync(Snowflake discordUser, CancellationToken ct = default)
    {
        var embed = new Embed
        {
            Colour      = _feedback.Theme.Warning,
            Description =
                "Hello there! This appears to be the first time you're using the bot (or you've not granted your "
                + "consent for it to store potentially sensitive or identifiable data about you).\n"
                + "\n"
                + "In order to use Amby and her commands, you need to give her your consent to store various data "
                + "about you. We need this consent in order to be compliant with data regulations in the European "
                + "Union (and it'd be rude not to ask!).\n"
                + "\n"
                + "In short, if you use the bot, we're going to be storing "
                + "stuff like your Discord ID, some messages, server IDs, etc. You can - and should! - read the "
                + "full privacy policy before you agree to anything. It's not very long (3 pages) and shouldn't take "
                + "more than five minutes to read through.\n"
                + "\n"
                + "Once you've read it, you can grant consent by running the `!privacy grant-consent` command over DM. "
                + "If you don't want to consent to anything, just don't use the bot :smiley:"
        };

        var openDM = await _userAPI.CreateDMAsync(discordUser, ct);

        if (openDM.IsSuccess)
        {
            var channel     = openDM.Entity;
            var sendMessage = await _channelAPI.CreateMessageAsync(channel.ID, embeds : new[] { embed }, ct : ct);

            if (sendMessage.IsSuccess)
            {
                return(await SendPrivacyPolicyAsync(channel.ID, ct));
            }
        }

        const string warningMessage = "I was unable to send you some initial privacy policy information for "
                                      + "first-time users of the bot. In order to use the bot, please read the "
                                      + "privacy policy (which can be requested by running the `!privacy policy` "
                                      + "command) and agree to it by sending `!privacy grant-consent` to the bot over"
                                      + " DM.";

        var sendWarning = await _feedback.SendContextualWarningAsync(warningMessage, discordUser, ct : ct);

        return(!sendWarning.IsSuccess
            ? Result.FromError(sendWarning)
            : Result.FromSuccess());
    }
Exemplo n.º 5
0
        public async Task <IResult> ListAsync()
        {
            var canViewPatterns = _messageContentPatternService.CanViewPatterns();

            if (_context is not MessageContext)
            {
                return(Result.FromError(new InvalidOperationError("Not a message context")));
            }

            if (!canViewPatterns)
            {
                await _channelApi.CreateMessageAsync(_context.ChannelID, $"<@!{_context.User.ID}> does not have permission to view patterns blocked or allowed in guild {_context.GuildID.Value.Value}!", allowedMentions : new NoAllowedMentions());

                return(Result.FromError(new InvalidOperationError("You do not have permission to view patterns blocked or allowed in this guild!")));
            }

            var patterns = await _messageContentPatternService.GetPatterns(_context.GuildID.Value.Value);

            if (!patterns.Any())
            {
                await _channelApi.CreateMessageAsync(_context.ChannelID, $"Guild {_context.GuildID.Value.Value} does not have any patterns set up, get started with `!pattern block` or `!pattern allow`");

                return(Result.FromError(new InvalidOperationError("This guild does not have any patterns set up, get started with `!pattern block` or `!pattern allow`")));
            }

            var blocked = patterns.Any(x => x.Type == MessageContentPatternType.Blocked)
                ? string.Join(Environment.NewLine, patterns.Where(x => x.Type == MessageContentPatternType.Blocked).Select(x => $"- `{x.Pattern}`"))
                : "There are no blocked patterns";

            var allowed = patterns.Any(x => x.Type == MessageContentPatternType.Allowed)
                ? string.Join(Environment.NewLine, patterns.Where(x => x.Type == MessageContentPatternType.Allowed).Select(x => $"- `{x.Pattern}`"))
                : "There are no allowed patterns";
            var guild = await _guildApi.GetGuildAsync(_context.GuildID.Value);

            var embed = new Embed(
                Title: $"Message Patterns for {guild.Entity!.Name}",
                Description: new Optional <string>("Allowed patterns supersede those that are blocked."),
                Fields: Enumerable.Empty <IEmbedField>().Concat(blocked.EnumerateLongTextAsFieldBuilders("Blocked"))
                .Concat(allowed.EnumerateLongTextAsFieldBuilders("Allowed")).ToList()
                );

            await _channelApi.CreateMessageAsync(_context.ChannelID, embeds : new[] { embed });

            return(Result.FromSuccess());
        }
Exemplo n.º 6
0
    /// <inheritdoc />
    public async Task <Result> RespondAsync(IMessageCreate ev, CancellationToken ct = default)
    {
        // return if the message has no screenshot;
        if (ev.Attachments.Count == 0 ||
            ev.Author.IsBot.HasValue && ev.Author.IsBot.Value ||
            ev.Author.IsSystem.HasValue && ev.Author.IsSystem.Value ||
            !ev.GuildID.HasValue
            )
        {
            return(Result.FromSuccess());
        }

        var getChannelNameResult = await _channelApi.GetChannelAsync(ev.ChannelID, ct);

        if (!getChannelNameResult.IsSuccess)
        {
            return(Result.FromError(getChannelNameResult));
        }

        var channelName = getChannelNameResult.Entity.Name;

        if (!channelName.HasValue)
        {
            return(new PropertyMissingOrNullError("Channel in which a potential application was sent has no name."));
        }

        // return if the message isn't in #member-apps;
        if (!channelName.Value.Equals(_discordSettings.ChannelNames.MemberApps))
        {
            return(Result.FromSuccess());
        }

        var createMemberAppResult = await _mediator.Send(
            new CreateFromDiscordMessage.Command {
            DiscordMessageCreatedEvent = ev
        }, ct);

        if (createMemberAppResult.IsSuccess)
        {
            _logger.LogInformation($"Added a new application for message: {ev.ID}");
        }
        else
        {
            return(createMemberAppResult);
        }

        var sendConfirmationResult = await _channelApi.CreateMessageAsync(ev.ChannelID,
                                                                          "Your application has been submitted and you will be pinged once it has been processed.",
                                                                          messageReference : new MessageReference(ev.ID), ct : ct);

        return(sendConfirmationResult.IsSuccess
            ? Result.FromSuccess()
            : Result.FromError(sendConfirmationResult.Error));
    }
        protected override async Task Handle(TcpRequest <GenericCommandResult> request, CancellationToken cancellationToken)
        {
            var msg = request.Message;
            // we want an exception if failed, as the handler can't proceed if failed, hence Parse instead of TryParse;
            var parsedId         = ulong.Parse(request.Message.DiscordChannelId);
            var channelSnowflake = new Snowflake(parsedId);
            var getChannelResult = await _channelApi.GetChannelAsync(channelSnowflake, cancellationToken);

            if (!getChannelResult.IsSuccess || getChannelResult.Entity is null)
            {
                throw new Exception(getChannelResult.Error?.Message ?? $"Could not get channel with ID {parsedId}");
            }

            // because why would ColorTranslator use the established pattern of TryParse
            // when it can have only one method that throws if it fails to parse instead
            // FFS
            Color colour;

            try
            {
                colour = ColorTranslator.FromHtml(request.Message.Colour);
            }
            catch
            {
                colour = _colourPalette.Blue;
            }

            var sendCmdExecutedNotificationResult = await _channelApi.CreateMessageAsync(
                channelSnowflake,
                content : $"[{msg.ServerId}] Command `{msg.Command}` executed!",
                ct : cancellationToken);

            if (!sendCmdExecutedNotificationResult.IsSuccess)
            {
                _logger.LogError("Error while sending command output embed: {Error}",
                                 sendCmdExecutedNotificationResult.Error.Message);
            }

            var embeds = new List <Embed>();
            var output = msg.CommandOutput
                         .Chunk(1024)
                         .Select(chars => new string(chars.ToArray()))
                         .ToList();

            embeds
            .AddRange(output
                      .Select((str, i) => new Embed
            {
                Title  = $"[{i + 1}/{output.Count}] Command `{msg.Command}`'s output",
                Fields = new List <EmbedField>
                {
                    new("Output message", string.IsNullOrEmpty(str) ? "*No output*" : str !, false)
                },
Exemplo n.º 8
0
        public async Task <Result> PostHttpCatAsync([Description("The HTTP code.")] int httpCode)
        {
            var embedImage = new EmbedImage($"https://http.cat/{httpCode}");
            var embed      = new Embed(Image: embedImage);

            var reply = await _channelAPI.CreateMessageAsync
                        (
                _context.ChannelID,
                embed : embed,
                ct : this.CancellationToken
                        );

            return(!reply.IsSuccess
                ? Result.FromError(reply)
                : Result.FromSuccess());
        }
    /// <inheritdoc />
    public async Task <Result> RespondAsync(IGuildMemberAdd ev, CancellationToken ct = default)
    {
        var getLogsChannelResult =
            await _guildApi.FindGuildChannelByName(ev.GuildID, _discordSettings.ChannelNames.LogsSpam);

        if (!getLogsChannelResult.IsSuccess)
        {
            return(Result.FromError(getLogsChannelResult.Error));
        }

        if (!ev.User.HasValue ||
            ev.User.Value is null ||
            ev.User.Value.IsBot.HasValue && ev.User.Value.IsBot.Value ||
            ev.User.Value.IsSystem.HasValue && ev.User.Value.IsSystem.Value
            )
        {
            return(Result.FromSuccess());
        }

        var user           = ev.User.Value;
        var iconUrl        = new Optional <string>();
        var embedThumbnail = new Optional <IEmbedThumbnail>();

        if (user.Avatar?.Value is not null)
        {
            var url = $"https://cdn.discordapp.com/avatars/{user.ID.Value}/{user.Avatar.Value}.png";
            iconUrl        = url;
            embedThumbnail = new EmbedThumbnail(url);
        }

        var embed = new Embed
        {
            Author      = new EmbedAuthor($"{user.Username}#{user.Discriminator}", $"https://discord.com/users/{user.ID}", iconUrl),
            Description = $":inbox_tray: <@{user.ID}> joined the server.",
            Thumbnail   = embedThumbnail,
            Colour      = _colourPalette.Green,
            Footer      = new EmbedFooter($"ID: {user.ID}"),
            Timestamp   = DateTimeOffset.UtcNow
        };
        var sendMessageResult =
            await _channelApi.CreateMessageAsync(getLogsChannelResult.Entity.ID, embeds : new[] { embed }, ct : ct);

        return(!sendMessageResult.IsSuccess
            ? Result.FromError(sendMessageResult)
            : Result.FromSuccess());
    }
Exemplo n.º 10
0
        /// <inheritdoc />
        public async Task <Result> RespondAsync(IMessageCreate gatewayEvent, CancellationToken ct = default)
        {
            if ((gatewayEvent.Author.IsBot.HasValue && gatewayEvent.Author.IsBot.Value) ||
                (gatewayEvent.Author.IsSystem.HasValue && gatewayEvent.Author.IsSystem.Value))
            {
                return(Result.FromSuccess());
            }

            var replyResult = await _channelAPI.CreateMessageAsync
                              (
                gatewayEvent.ChannelID,
                gatewayEvent.Content,
                ct : ct
                              );

            return(replyResult.IsSuccess
                ? Result.FromSuccess()
                : Result.FromError(replyResult));
        }
Exemplo n.º 11
0
        /// <inheritdoc />
        public async Task <Result <ModerationAction> > Handle(Command request, CancellationToken cancellationToken)
        {
            var ma = request.ModerationAction;

            if (ma.ModerationActionType != ModerationActionType.Ban)
            {
                return(new UnsupportedArgumentError(
                           $"Wrong moderation action type. Expected: {ModerationActionType.Ban}, got: {ma.ModerationActionType}"));
            }
            //if (!ma.IsActive) return new ValidationError("Moderation action is already inactive.");

            if (ma.UserIgn is not null)
            {
                var proto = new GenericCommand
                {
                    DefaultCommand     = "ban",
                    DiscordCommandName = "ban",
                    DiscordChannelId   = request.ChannelId.ToString(),
                    Args = { request.ModerationAction.UserIgn }
                };
                await _ps.BroadcastMessage(proto);
            }

            if (request.ModerationAction.UserDiscordId is not null)
            {
                var userDiscordIdSnowflake = new Snowflake(request.ModerationAction.UserDiscordId.Value);
                var banResult = await _guildApi.RemoveGuildBanAsync(
                    new(request.ModerationAction.GuildId),
                    new(request.ModerationAction.UserDiscordId.Value),
                    new(),
                    cancellationToken
                    );

                if (!banResult.IsSuccess)
                {
                    return(Result <ModerationAction> .FromError(banResult.Error));
                }

                var embed = new Embed
                {
                    Title     = "You have been unbanned from Modded Minecraft Club.",
                    Colour    = _colourPalette.Green,
                    Thumbnail = EmbedProperties.MmccLogoThumbnail
                };

                var createDmResult = await _userApi.CreateDMAsync(userDiscordIdSnowflake, cancellationToken);

                const string warningMsg =
                    "Failed to send a DM notification to the user. It may be because they have blocked the bot or don't share any servers. This warning can in most cases be ignored.";
                if (!createDmResult.IsSuccess || createDmResult.Entity is null)
                {
                    _logger.LogWarning(warningMsg);
                }
                else
                {
                    var sendDmResult = await _channelApi.CreateMessageAsync(createDmResult.Entity.ID,
                                                                            embeds : new[] { embed },
                                                                            ct : cancellationToken);

                    if (!sendDmResult.IsSuccess)
                    {
                        _logger.LogWarning(warningMsg);
                    }
                }
            }

            try
            {
                ma.IsActive   = false;
                ma.ExpiryDate = DateTimeOffset.Now.ToUnixTimeMilliseconds();
                await _context.SaveChangesAsync(cancellationToken);
            }
            catch (Exception e)
            {
                return(e);
            }

            return(ma);
        }
Exemplo n.º 12
0
        public async Task <Result> AddNewVodConfigurationAsync()
        {
            if (_context.Message.Attachments.HasValue)
            {
                await _channelApi.CreateMessageAsync(_context.ChannelID,
                                                     "It appears you may have given me an existing JSON configuration! Validating...");

                var result = await _configurationService.TryParseVodJsonConfigurationAsync(
                    _context.Message.Attachments.Value.FirstOrDefault(x => x.Filename.Contains(".json")));

                if (!result.IsSuccess)
                {
                    await _channelApi.CreateMessageAsync(_context.ChannelID,
                                                         $"JSON configuration failed! Reason: {result.ErrorMessage}");

                    return(new UserError(result.ErrorMessage ?? "Malformed or Nonexistent JSON. No specific error returned."));
                }

                var vodConfiguration = result.VodConfiguration;

                var sb = new StringBuilder();
                sb.AppendLine("**Configuration successfully validated!**");
                sb.AppendLine("**This is what I have understood from your configuration file:**");
                sb.AppendLine();
                sb.AppendLine("**Game Name:**");
                sb.AppendLine(vodConfiguration !.GameName);
                sb.AppendLine();

                for (var i = 0; i < vodConfiguration.Questions.Count; i++)
                {
                    var question = vodConfiguration.Questions[i];
                    sb.Append($"**Question ");
                    sb.Append(i);
                    sb.AppendLine(":**");
                    sb.AppendLine(question.QuestionText);
                    sb.AppendLine("**Answer Kind:**");
                    sb.AppendLine(question.UserAnswerKind.ToString());

                    switch (question.UserAnswerKind)
                    {
                    case AnswerKind.Text:
                        break;

                    case AnswerKind.Integer:
                        break;

                    case AnswerKind.Decimal:
                        break;

                    case AnswerKind.CustomScale:
                        sb.AppendLine("**Scale Values:**");

                        sb.Append(question.MinimumScale !.Value);
                        sb.Append(" Minimum, ");
                        sb.Append(question.MaximumScale !.Value);
                        sb.AppendLine(" Maximum.");
                        break;

                    case AnswerKind.Percentage:
                        break;

                    case AnswerKind.MultipleChoice:
                        sb.AppendLine("**Answer Options:**");
                        foreach (var answerOption in question.AnswerOptions !)
                        {
                            sb.Append(answerOption.Id);
                            sb.Append(" - ");
                            sb.Append(answerOption.OptionTitle);

                            if (answerOption.ShouldRejectIfChosen)
                            {
                                sb.Append(" - ATTENTION: This option will cause an auto rejection!");
                            }

                            sb.AppendLine();
                        }

                        break;

                    case AnswerKind.Url:
                        break;

                    default:
                        throw new ArgumentOutOfRangeException();
                    }

                    sb.AppendLine();
                }

                await _channelApi.CreateMessageAsync(_context.ChannelID, sb.ToString());

                _configurationService.Save(vodConfiguration);
                return(Result.FromSuccess());
            }

            //TODO: Implement Q&A format

            await _channelApi.CreateMessageAsync(_context.ChannelID,
                                                 "It appears no configuration file has been provided. See `help vodconfig add` for configuration file instructions.");

            return(new UserError("No vodconfig provided."));
        }
Exemplo n.º 13
0
            /// <inheritdoc />
            public async Task <Result <ModerationAction> > Handle(Command request, CancellationToken cancellationToken)
            {
                var banModerationAction = new ModerationAction
                                          (
                    moderationActionType: ModerationActionType.Ban,
                    guildId: request.GuildId.Value,
                    isActive: true,
                    reason: request.Reason,
                    date: DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(),
                    userDiscordId: request.UserDiscordId?.Value,
                    userIgn: request.UserIgn,
                    expiryDate: request.ExpiryDate
                                          );

                if (request.UserIgn is not null)
                {
                    var proto = new GenericCommand
                    {
                        DefaultCommand     = "ban",
                        DiscordCommandName = "ban",
                        DiscordChannelId   = request.ChannelId.Value.ToString(),
                        Args = { request.UserIgn }
                    };
                    await _ps.BroadcastMessage(proto);
                }

                if (request.UserDiscordId is not null)
                {
                    var guildResult = await _guildApi.GetGuildAsync(request.GuildId, ct : cancellationToken);

                    if (!guildResult.IsSuccess)
                    {
                        return(Result <ModerationAction> .FromError(guildResult.Error));
                    }

                    var banResult = await _guildApi.CreateGuildBanAsync(
                        request.GuildId,
                        request.UserDiscordId.Value,
                        reason : request.Reason,
                        ct : cancellationToken
                        );

                    if (!banResult.IsSuccess)
                    {
                        return(Result <ModerationAction> .FromError(banResult.Error));
                    }

                    var guildName = guildResult.Entity.Name;
                    var embed     = new Embed
                    {
                        Title     = $"You have been banned from {guildName}.",
                        Colour    = _colourPalette.Red,
                        Thumbnail = EmbedProperties.MmccLogoThumbnail,
                        Timestamp = DateTimeOffset.UtcNow,
                        Fields    = new List <EmbedField>
                        {
                            new("Reason", request.Reason, false),
                            new(
                                "Expires at",
                                request.ExpiryDate is null
                                    ? "Permanent"
                                    : $"{DateTimeOffset.FromUnixTimeMilliseconds(request.ExpiryDate.Value).UtcDateTime} UTC"
                                ,
                                false
                                ),
                            new(
                                "Appeals",
                                $"You can appeal this decision **[here]({_discordSettings.AppealsUrl})**."
                                )
                        }
                    };

                    var createDmResult = await _userApi.CreateDMAsync(request.UserDiscordId.Value, cancellationToken);

                    const string errMsg =
                        "Failed to send a DM notification to the user. It may be because they have blocked the bot. This error can in most cases be ignored.";
                    if (!createDmResult.IsSuccess || createDmResult.Entity is null)
                    {
                        _logger.LogWarning(errMsg);
                    }
                    else
                    {
                        var sendDmResult = await _channelApi.CreateMessageAsync(createDmResult.Entity.ID,
                                                                                embeds : new[] { embed },
                                                                                ct : cancellationToken);

                        if (!sendDmResult.IsSuccess)
                        {
                            _logger.LogWarning(errMsg);
                        }
                    }
                }

                try
                {
                    await _context.AddAsync(banModerationAction, cancellationToken);

                    await _context.SaveChangesAsync(cancellationToken);
                }
                catch (Exception e)
                {
                    return(e);
                }

                return(banModerationAction);
            }
Exemplo n.º 14
0
 /// <inheritdoc />
 public async Task <IResult> Respond(string message) =>
 await _channelApi.CreateMessageAsync(
     channelID : _context.ChannelID,
     content : message,
     messageReference : new MessageReference(_context.MessageID, FailIfNotExists: false)
     );
Exemplo n.º 15
0
 public Task <Result <IMessage> > PingAsync() => _channelApi.CreateMessageAsync(_context.ChannelID, "Pong!");
Exemplo n.º 16
0
    /// <inheritdoc />
    public async Task <Result> RespondAsync(IGuildMemberAdd gatewayEvent, CancellationToken ct = default)
    {
        if (!gatewayEvent.User.IsDefined(out var user))
        {
            // We can't do anything about this
            return(Result.FromSuccess());
        }

        var getServerResult = await _servers.GetOrRegisterServerAsync(gatewayEvent.GuildID, ct);

        if (!getServerResult.IsSuccess)
        {
            return(Result.FromError(getServerResult));
        }

        var server = getServerResult.Entity;

        if (!server.SendJoinMessage)
        {
            return(Result.FromSuccess());
        }

        var getJoinMessageResult = ServerService.GetJoinMessage(server);

        if (!getJoinMessageResult.IsSuccess)
        {
            return(Result.FromError(getJoinMessageResult));
        }

        var openDM = await _userAPI.CreateDMAsync(user.ID, ct);

        if (!openDM.IsSuccess)
        {
            return(Result.FromError(openDM));
        }

        var userChannel = openDM.Entity;

        var embed = new Embed
        {
            Colour      = _feedback.Theme.Secondary,
            Description = $"Welcome, <@{user.ID}>!\n" +
                          "\n" +
                          $"{getJoinMessageResult.Entity}"
        };

        var sendEmbed = await _channelAPI.CreateMessageAsync(userChannel.ID, embeds : new[] { embed }, ct : ct);

        if (sendEmbed.IsSuccess)
        {
            return(Result.FromSuccess());
        }

        if (sendEmbed.Error is not RestResultError <RestError> re)
        {
            return(Result.FromError(sendEmbed));
        }

        if (re.Error.Code is not DiscordError.CannotSendMessageToUser)
        {
            return(Result.FromError(sendEmbed));
        }

        var getGuild = await _guildAPI.GetGuildAsync(gatewayEvent.GuildID, ct : ct);

        if (!getGuild.IsSuccess)
        {
            return(Result.FromError(getGuild));
        }

        var guild = getGuild.Entity;

        if (!guild.SystemChannelID.HasValue)
        {
            // man, we tried
            return(Result.FromSuccess());
        }

        var content = $"Welcome, <@{user.ID}>! You have DMs disabled, so I couldn't send you " +
                      "the first-join message. To see it, type \"!server join-message\".";

        var sendNotification = await _feedback.SendWarningAsync
                               (
            guild.SystemChannelID.Value,
            content,
            user.ID,
            ct : ct
                               );

        return(sendNotification.IsSuccess
            ? Result.FromSuccess()
            : Result.FromError(sendNotification));
    }
Exemplo n.º 17
0
        /// <inheritdoc />
        public async Task <Result <ModerationAction> > Handle(Command request, CancellationToken cancellationToken)
        {
            var warnModerationAction = new ModerationAction
                                       (
                moderationActionType: ModerationActionType.Warn,
                guildId: request.GuildId.Value,
                isActive: true,
                reason: request.Reason,
                date: DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(),
                userDiscordId: request.UserDiscordId?.Value,
                userIgn: request.UserIgn,
                expiryDate: null
                                       );

            if (request.UserIgn is not null)
            {
                var protobufMessage = new ChatMessage
                {
                    ServerId      = "MMCC",
                    Message       = $"You have been warned, @{request.UserIgn}. Reason: {request.Reason}",
                    MessageOffset = 5
                };
                await _ps.BroadcastMessage(protobufMessage);
            }

            if (request.UserDiscordId is not null)
            {
                var guildResult = await _guildApi.GetGuildAsync(request.GuildId, ct : cancellationToken);

                if (!guildResult.IsSuccess)
                {
                    return(Result <ModerationAction> .FromError(guildResult.Error));
                }

                var guildName = guildResult.Entity.Name;
                var embed     = new Embed
                {
                    Title     = $"You have been warned in {guildName}.",
                    Colour    = _colourPalette.Yellow,
                    Thumbnail = EmbedProperties.MmccLogoThumbnail,
                    Timestamp = DateTimeOffset.UtcNow,
                    Fields    = new List <EmbedField>
                    {
                        new("Reason", request.Reason, false)
                    }
                };

                var createDmResult = await _userApi.CreateDMAsync(request.UserDiscordId.Value, cancellationToken);

                const string errMsg =
                    "Failed to send a DM notification to the user. It may be because they have blocked the bot. This error can in most cases be ignored.";
                if (!createDmResult.IsSuccess || createDmResult.Entity is null)
                {
                    _logger.LogWarning(errMsg);
                }
                else
                {
                    var sendDmResult = await _channelApi.CreateMessageAsync(createDmResult.Entity.ID,
                                                                            embeds : new[] { embed },
                                                                            ct : cancellationToken);

                    if (!sendDmResult.IsSuccess)
                    {
                        _logger.LogWarning(errMsg);
                    }
                }
            }

            try
            {
                await _context.AddAsync(warnModerationAction, cancellationToken);

                await _context.SaveChangesAsync(cancellationToken);
            }
            catch (Exception e)
            {
                return(e);
            }

            return(warnModerationAction);
        }
Exemplo n.º 18
0
    public async Task <Result> JumboAsync(IEmoji emoji)
    {
        string emoteUrl;

        if (emoji.ID is not null)
        {
            var getEmoteUrl = CDN.GetEmojiUrl(emoji);
            if (!getEmoteUrl.IsSuccess)
            {
                return(Result.FromError(getEmoteUrl));
            }

            emoteUrl = getEmoteUrl.Entity.ToString();
        }
        else
        {
            if (emoji.Name is null)
            {
                return(new UserError("Looks like a bad emoji. Oops!"));
            }

            var emojiName = emoji.Name;
            if (EmojiMap.Map.TryGetValue(emoji.Name, out var mappedEmote))
            {
                emojiName = mappedEmote;
            }

            var hexValues = new List <string>();
            for (var i = 0; i < emojiName.Length; ++i)
            {
                var codepoint = char.ConvertToUtf32(emojiName, i);

                // 0xFE0F is a variation marker, which explicitly requests a colourful version of the emoji, and
                // not a monochrome text variant. Since Twemoji only provides the colourful ones, we can safely
                // skip it.
                if (codepoint == 0xFE0F)
                {
                    continue;
                }

                var codepointHex = codepoint.ToString("x");
                hexValues.Add(codepointHex);

                // ConvertToUtf32() might have parsed an extra character as some characters are combinations of two
                // 16-bit characters which start at 0x00d800 and end at 0x00dfff (Called surrogate low and surrogate
                // high)
                //
                // If the character is in this span, we have already essentially parsed the next index of the string
                // as well. Therefore we make sure to skip the next one.
                if (char.IsSurrogate(emojiName, i))
                {
                    ++i;
                }
            }

            var emojiCode = string.Join("-", hexValues);
            emoteUrl = $"https://raw.githubusercontent.com/twitter/twemoji/master/assets/72x72/{emojiCode}.png";
        }

        var response = await _httpClient.GetAsync(emoteUrl, HttpCompletionOption.ResponseHeadersRead);

        if (!response.IsSuccessStatusCode)
        {
            return(new UserError("Sorry, I couldn't find that emote."));
        }

        var embed = new Embed
        {
            Colour = Color.MediumPurple,
            Image  = new EmbedImage(emoteUrl)
        };

        var sendEmoji = await _channelAPI.CreateMessageAsync
                        (
            _context.ChannelID,
            embeds : new[] { embed },
            ct : this.CancellationToken
                        );

        return(sendEmoji.IsSuccess ? Result.FromSuccess() : Result.FromError(sendEmoji));
    }