예제 #1
0
        public async Task BuildRemovableEmbed(IMessage message, IUser executingUser, Func <EmbedBuilder, Task <IUserMessage> > callback)
        {
            var embed = BuildQuoteEmbed(message, executingUser);

            if (callback == null || embed == null)
            {
                return;
            }

            await _autoRemoveMessageService.RegisterRemovableMessageAsync(executingUser, embed,
                                                                          async (e) => await callback.Invoke(e));
        }
예제 #2
0
        public async Task IsUp([Summary("Url to ping")] string url)
        {
            var message = await ReplyAsync("", embed : new EmbedBuilder()
                                           .WithTitle($"Checking status of {url}")
                                           .WithUserAsAuthor(Context.User)
                                           .WithColor(Color.Orange)
                                           .Build());

            var resp = await _isUpService.GetIsUpResponseAsync(url);


            if (resp.StatusString != "OK")
            {
                await message.DeleteAsync();
                await ReplyAsync($"Error {resp.StatusString}: Something Went Wrong Querying the IsItDown API ");

                return;
            }

            EmbedBuilder builder;

            if (resp.Host == null || resp.ResponseCode == null)
            {
                builder = new EmbedBuilder()
                          .WithTitle($"Host: { (resp.Host != null ? $"```{resp.Host}```" : "No Host found")}")
                          .WithUserAsAuthor(Context.User)
                          .WithColor(Color.Red)
                          .WithDescription($"Something went wrong querying: `{url}` Is that a valid URL?");
            }
            else
            {
                builder = new EmbedBuilder()
                          .WithTitle($"Host: { (resp.Host != null ? $"```{resp.Host}```" : "No Host found")}")
                          .WithUserAsAuthor(Context.User)
                          .WithColor(Color.Green)
                          .AddField("Status ", $"{(resp.Isitdown ? "❌" : "✅")} {resp.ResponseCode}", true);
            }

            await _autoRemoveMessageService.RegisterRemovableMessageAsync(Context.User, builder, async (e) =>
            {
                await message.ModifyAsync(a =>
                {
                    a.Content = string.Empty;
                    a.Embed   = e.Build();
                });
                return(message);
            });

            await Context.Message.DeleteAsync();
        }
예제 #3
0
        public async Task LinkAsync(
            [Summary("The link to shorten.")]
            Uri uri)
        {
            var host = uri.Host;

            if (!_allowedHosts.Contains(host))
            {
                await ReplyAsync(embed : new EmbedBuilder()
                                 .WithColor(Color.Red)
                                 .WithDescription($"Links to {host} cannot be shortened.")
                                 .Build());

                return;
            }

            var urlMarkdown = Format.Url($"{host} (click here)", uri.ToString());

            var description = host.Equals("sharplab.io") && TryPrepareSharplabPreview(uri.OriginalString, urlMarkdown.Length + 1, out var preview)
                ? $"{urlMarkdown}\n{preview}"
                : urlMarkdown;

            if (description.Length > EmbedBuilder.MaxDescriptionLength)
            {
                await ReplyAsync("Error: The provided link is too long to be converted to an embed.");

                return;
            }

            var embed = new EmbedBuilder()
                        .WithDescription(description)
                        .WithUserAsAuthor(Context.User)
                        .WithColor(Color.LightGrey);

            await _autoRemoveMessageService.RegisterRemovableMessageAsync(Context.User, embed, async e => await ReplyAsync(embed: e.Build()));

            await Context.Message.DeleteAsync();
        }
예제 #4
0
        public async Task GetUserInfoAsync(
            [Summary("The user to retrieve information about, if any.")]
            [Remainder] DiscordUserOrMessageAuthorEntity user = null)
        {
            var userId = user?.UserId ?? Context.User.Id;

            var timer = Stopwatch.StartNew();

            var userInfo = await _userService.GetUserInformationAsync(Context.Guild.Id, userId);

            if (userInfo == null)
            {
                var embed = new EmbedBuilder()
                            .WithTitle("Retrieval Error")
                            .WithColor(Color.Red)
                            .WithDescription("Sorry, we don't have any data for that user - and we couldn't find any, either.")
                            .AddField("User Id", userId);
                await _autoRemoveMessageService.RegisterRemovableMessageAsync(
                    Context.User,
                    embed,
                    async (embedBuilder) => await ReplyAsync(embed: embedBuilder.Build()));

                return;
            }

            var builder = new StringBuilder();

            builder.AppendLine("**\u276F User Information**");
            builder.AppendLine("ID: " + userId);
            builder.AppendLine("Profile: " + MentionUtils.MentionUser(userId));

            var embedBuilder = new EmbedBuilder()
                               .WithUserAsAuthor(userInfo)
                               .WithTimestamp(_utcNow);

            var avatar = userInfo.GetDefiniteAvatarUrl();

            embedBuilder.ThumbnailUrl   = avatar;
            embedBuilder.Author.IconUrl = avatar;

            ValueTask <Color> colorTask = default;

            if ((userInfo.GetAvatarUrl(size: 16) ?? userInfo.GetDefaultAvatarUrl()) is { } avatarUrl)
            {
                colorTask = _imageService.GetDominantColorAsync(new Uri(avatarUrl));
            }

            var moderationRead = await _authorizationService.HasClaimsAsync(Context.User as IGuildUser, AuthorizationClaim.ModerationRead);

            var promotions = await _promotionsService.GetPromotionsForUserAsync(Context.Guild.Id, userId);

            if (userInfo.IsBanned)
            {
                builder.AppendLine("Status: **Banned** \\🔨");

                if (moderationRead)
                {
                    builder.AppendLine($"Ban Reason: {userInfo.BanReason}");
                }
            }

            if (userInfo.FirstSeen is DateTimeOffset firstSeen)
            {
                builder.AppendLine($"First Seen: {FormatUtilities.FormatTimeAgo(_utcNow, firstSeen)}");
            }

            if (userInfo.LastSeen is DateTimeOffset lastSeen)
            {
                builder.AppendLine($"Last Seen: {FormatUtilities.FormatTimeAgo(_utcNow, lastSeen)}");
            }

            try
            {
                var userRank = await _messageRepository.GetGuildUserParticipationStatistics(Context.Guild.Id, userId);

                var messagesByDate = await _messageRepository.GetGuildUserMessageCountByDate(Context.Guild.Id, userId, TimeSpan.FromDays(30));

                var messageCountsByChannel = await _messageRepository.GetGuildUserMessageCountByChannel(Context.Guild.Id, userId, TimeSpan.FromDays(30));

                var emojiCounts = await _emojiRepository.GetEmojiStatsAsync(Context.Guild.Id, SortDirection.Ascending, 1, userId : userId);

                await AddParticipationToEmbedAsync(userId, builder, userRank, messagesByDate, messageCountsByChannel, emojiCounts);
            }
            catch (Exception ex)
            {
                _log.LogError(ex, "An error occured while retrieving a user's message count.");
            }

            AddMemberInformationToEmbed(userInfo, builder);
            AddPromotionsToEmbed(builder, promotions);

            if (moderationRead)
            {
                await AddInfractionsToEmbedAsync(userId, builder);
            }

            embedBuilder.Description = builder.ToString();

            embedBuilder.WithColor(await colorTask);

            timer.Stop();
            embedBuilder.WithFooter(footer => footer.Text = $"Completed after {timer.ElapsedMilliseconds} ms");

            await _autoRemoveMessageService.RegisterRemovableMessageAsync(
                userInfo.Id == Context.User.Id?new[] { userInfo } : new[] { userInfo, Context.User },
                embedBuilder,
                async (embedBuilder) => await ReplyAsync(embed: embedBuilder.Build()));
        }
예제 #5
0
        public async Task ReplInvokeAsync(
            [Remainder]
            [Summary("The code to decompile.")]
            string code)
        {
            if (!(Context.Channel is IGuildChannel))
            {
                await ReplyAsync("il can only be executed in public guild channels.");

                return;
            }
            code = FormatUtilities.StripFormatting(code);
            if (code.Length > 1000)
            {
                await ReplyAsync("Decompile Failed: Code is greater than 1000 characters in length");

                return;
            }

            var guildUser = Context.User as IGuildUser;
            var message   = await Context.Channel.SendMessageAsync("Working...");

            var content = FormatUtilities.BuildContent(code);

            HttpResponseMessage res;

            try
            {
                var client = _httpClientFactory.CreateClient();

                using (var tokenSrc = new CancellationTokenSource(30000))
                {
                    res = await client.PostAsync(_ilUrl, content, tokenSrc.Token);
                }
            }
            catch (TaskCanceledException)
            {
                await message.ModifyAsync(a => { a.Content = $"Gave up waiting for a response from the Decompile service."; });

                return;
            }
            catch (Exception ex)
            {
                await message.ModifyAsync(a => { a.Content = $"Decompile failed: {ex.Message}"; });

                Log.Error(ex, "Decompile Failed");
                return;
            }

            if (!res.IsSuccessStatusCode & res.StatusCode != HttpStatusCode.BadRequest)
            {
                await message.ModifyAsync(a => { a.Content = $"Decompile failed: {res.StatusCode}"; });

                return;
            }

            var parsedResult = await res.Content.ReadAsStringAsync();

            var embed = await BuildEmbedAsync(guildUser, code, parsedResult);

            await _autoRemoveMessageService.RegisterRemovableMessageAsync(Context.User, embed, async (e) =>
            {
                await message.ModifyAsync(a =>
                {
                    a.Content = string.Empty;
                    a.Embed   = e.Build();
                });
                return(message);
            });

            await Context.Message.DeleteAsync();
        }
예제 #6
0
        protected override async Task Handle(UserInfoCommand request, CancellationToken cancellationToken)
        {
            var stopwatch = new Stopwatch();

            stopwatch.Start();

            _logger.LogDebug("Getting info for user {UserId} in guild {GuildId}", request.UserId, request.GuildId);

            var guild = await _discordClient.GetGuildAsync(request.GuildId, options : new RequestOptions
            {
                CancelToken = cancellationToken,
            });

            var discordUser = await guild.GetUserAsync(request.UserId);

            _logger.LogDebug("Got user {UserId} from Discord API", request.UserId);

            if (discordUser is null)
            {
                // If we have nothing from Discord or from the database,
                // we'll need to assume this user doesn't exist

                _logger.LogDebug("Discord user {UserId} is null, cannot return info, replying with retrieval error embed", request.UserId);

                var embed = new EmbedBuilder()
                            .WithTitle("Retrieval Error")
                            .WithColor(Color.Red)
                            .WithDescription("Sorry, we don't have any data for that user - and we couldn't find any, either.")
                            .AddField("User Id", request.UserId);

                await _autoRemoveMessageService.RegisterRemovableMessageAsync(
                    request.Message.Author,
                    embed,
                    async (builder) =>
                    await request.Message.ReplyAsync(string.Empty, embed: builder.Build()));

                stopwatch.Stop();

                return;
            }

            var modixUser = await GetUserAsync(request.UserId, request.GuildId, cancellationToken);

            var userInfoBuilder = UserInfoEmbedBuilderHelper.Create();

            userInfoBuilder
            .WithId(request.UserId)
            .WithClickableMention(request.UserId)
            .WithStatus(discordUser.Status)
            .WithFirstAndLastSeen(modixUser.FirstSeen, modixUser.LastSeen);

            if (modixUser.Infractions.Any(x => x.Type == InfractionType.Ban))
            {
                var banReason = modixUser
                                .Infractions
                                .Where(x => x.Type == InfractionType.Ban)
                                .OrderByDescending(x => x.Created)
                                .Select(x => x.Reason)
                                .First();

                userInfoBuilder.WithBan(banReason);
            }

            var participation = await GetParticipationStatisticsAsync(guild, request.UserId, cancellationToken);

            if (participation is { })
예제 #7
0
        public async Task ReplInvokeAsync(
            [Remainder]
            [Summary("The code to execute.")]
            string code)
        {
            if (!(Context.Channel is IGuildChannel) || !(Context.User is IGuildUser guildUser))
            {
                await ModifyOrSendErrorEmbed("The REPL can only be executed in public guild channels.");

                return;
            }

            var message = await Context.Channel
                          .SendMessageAsync(embed : new EmbedBuilder()
                                            .WithTitle("REPL Executing")
                                            .WithUserAsAuthor(Context.User)
                                            .WithColor(Color.LightOrange)
                                            .WithDescription($"Compiling and Executing [your code]({Context.Message.GetJumpUrl()})...")
                                            .Build());

            var content = FormatUtilities.BuildContent(code);

            // make it easier to trace calls back to discord if moderation or investigation needs to happen
            content.Headers.TryAddWithoutValidation("X-Modix-DiscordUserId", Context.User.Id.ToString());
            content.Headers.TryAddWithoutValidation("X-Modix-DiscordUsername", Context.User.GetFullUsername());
            var messageLink = $"https://discord.com/channels/{Context.Guild.Id}/{Context.Channel.Id}/{message.Id}";

            content.Headers.TryAddWithoutValidation("X-Modix-MessageLink", messageLink);

            HttpResponseMessage res;

            try
            {
                var client = _httpClientFactory.CreateClient(HttpClientNames.RetryOnTransientErrorPolicy);
                res = await client.PostAsync(_replUrl, content);
            }
            catch (IOException ex)
            {
                await ModifyOrSendErrorEmbed("Recieved an invalid response from the REPL service." +
                                             $"\n\n{Format.Bold("Details:")}\n{ex.Message}", message);

                return;
            }
            catch (Exception ex)
            {
                await ModifyOrSendErrorEmbed("An error occurred while sending a request to the REPL service. " +
                                             "This may be due to a StackOverflowException or exceeding the 30 second timeout." +
                                             $"\n\n{Format.Bold("Details:")}\n{ex.Message}", message);

                return;
            }

            if (!res.IsSuccessStatusCode & res.StatusCode != HttpStatusCode.BadRequest)
            {
                await ModifyOrSendErrorEmbed($"Status Code: {(int)res.StatusCode} {res.StatusCode}", message);

                return;
            }

            var parsedResult = JsonConvert.DeserializeObject <Result>(await res.Content.ReadAsStringAsync());

            var embed = await BuildEmbedAsync(guildUser, parsedResult);

            await _autoRemoveMessageService.RegisterRemovableMessageAsync(Context.User, embed, async (e) =>
            {
                await message.ModifyAsync(a =>
                {
                    a.Content = string.Empty;
                    a.Embed   = e.Build();
                });
                return(message);
            });

            await Context.Message.DeleteAsync();
        }
예제 #8
0
        public async Task ReplInvokeAsync(
            [Remainder]
            [Summary("The code to execute.")]
            string code)
        {
            if (!(Context.Channel is IGuildChannel) || !(Context.User is IGuildUser guildUser))
            {
                await ModifyOrSendErrorEmbed("The REPL can only be executed in public guild channels.");

                return;
            }

            var message = await Context.Channel
                          .SendMessageAsync(embed : new EmbedBuilder()
                                            .WithTitle("REPL Executing")
                                            .WithUserAsAuthor(Context.User)
                                            .WithColor(Color.LightOrange)
                                            .WithDescription($"Compiling and Executing [your code]({Context.Message.GetJumpUrl()})...")
                                            .Build());

            var content = FormatUtilities.BuildContent(code);

            HttpResponseMessage res;

            try
            {
                var client = _httpClientFactory.CreateClient();
                res = await client.PostAsync(_replUrl, content);
            }
            catch (IOException ex)
            {
                await ModifyOrSendErrorEmbed("Recieved an invalid response from the REPL service." +
                                             $"\n\n{Format.Bold("Details:")}\n{ex.Message}", message);

                return;
            }
            catch (Exception ex)
            {
                await ModifyOrSendErrorEmbed("An error occurred while sending a request to the REPL service. " +
                                             "This is probably due to container exhaustion - try again later." +
                                             $"\n\n{Format.Bold("Details:")}\n{ex.Message}", message);

                return;
            }

            if (!res.IsSuccessStatusCode & res.StatusCode != HttpStatusCode.BadRequest)
            {
                await ModifyOrSendErrorEmbed($"Status Code: {(int)res.StatusCode} {res.StatusCode}", message);

                return;
            }

            var parsedResult = JsonConvert.DeserializeObject <Result>(await res.Content.ReadAsStringAsync());

            var embed = await BuildEmbedAsync(guildUser, parsedResult);

            await _autoRemoveMessageService.RegisterRemovableMessageAsync(Context.User, embed, async (e) =>
            {
                await message.ModifyAsync(a =>
                {
                    a.Content = string.Empty;
                    a.Embed   = e.Build();
                });
                return(message);
            });

            await Context.Message.DeleteAsync();
        }
예제 #9
0
        public async Task GetDocumentationAsync(
            [Remainder]
            [Summary("The term to search for in the documentation.")]
            string term)
        {
            Regex reg = new Regex("^[0-9A-Za-z.<>]$");

            foreach (char c in term)
            {
                if (!reg.IsMatch(c.ToString()))
                {
                    //Double the escape char so discord will print it as well
                    string s = (c == '\\') ? "\\\\" : c.ToString();
                    await ReplyAsync($" '{s}' character is not allowed in the search, please try again.");

                    return;
                }
            }

            var response = await DocumentationService.GetDocumentationResultsAsync(term);

            if (response.Count == 0)
            {
                await ReplyAsync("Could not find documentation for your requested term.");

                return;
            }

            var embedCount = 0;

            var stringBuild = new StringBuilder();

            foreach (var res in response.Results.Take(3))
            {
                embedCount++;
                stringBuild.AppendLine($"**\u276F [{res.ItemKind}: {res.DisplayName}]({res.Url})**");
                stringBuild.AppendLine($"{res.Description}");
                stringBuild.AppendLine();

                if (embedCount == 3)
                {
                    stringBuild.Append(
                        $"{embedCount}/{response.Results.Count} results shown ~ [Click Here for more results](https://docs.microsoft.com/dotnet/api/?term={term})"
                        );
                }
            }
            var buildEmbed = new EmbedBuilder()
            {
                Description = stringBuild.ToString()
            }.WithColor(new Color(46, 204, 113));

            var message = await ReplyAsync(embed : buildEmbed.Build());

            await _autoRemoveMessageService.RegisterRemovableMessageAsync(Context.User, buildEmbed, async (e) =>
            {
                await message.ModifyAsync(a =>
                {
                    a.Content = string.Empty;
                    a.Embed   = e.Build();
                });
                return(message);
            });

            await Context.Message.DeleteAsync();
        }
예제 #10
0
        public async Task ReplInvoke([Remainder] string code)
        {
            if (!(Context.Channel is SocketGuildChannel))
            {
                await ReplyAsync("exec can only be executed in public guild channels.");

                return;
            }

            if (code.Length > 1000)
            {
                await ReplyAsync("Exec failed: Code is greater than 1000 characters in length");

                return;
            }

            var guildUser = Context.User as SocketGuildUser;
            var message   = await Context.Channel.SendMessageAsync("Working...");

            var content = FormatUtilities.BuildContent(code);

            HttpResponseMessage res;

            try
            {
                var client = _httpClientFactory.CreateClient();

                using (var tokenSrc = new CancellationTokenSource(30000))
                {
                    res = await _httpClientFactory.CreateClient().PostAsync(ReplRemoteUrl, content, tokenSrc.Token);
                }
            }
            catch (TaskCanceledException)
            {
                await message.ModifyAsync(a => { a.Content = $"Gave up waiting for a response from the REPL service."; });

                return;
            }
            catch (Exception ex)
            {
                await message.ModifyAsync(a => { a.Content = $"Exec failed: {ex.Message}"; });

                Log.Error(ex, "Exec Failed");
                return;
            }

            if (!res.IsSuccessStatusCode & res.StatusCode != HttpStatusCode.BadRequest)
            {
                await message.ModifyAsync(a => { a.Content = $"Exec failed: {res.StatusCode}"; });

                return;
            }

            var parsedResult = JsonConvert.DeserializeObject <Result>(await res.Content.ReadAsStringAsync());

            var embed = await BuildEmbed(guildUser, parsedResult);

            await message.ModifyAsync(a =>
            {
                a.Content = string.Empty;
                a.Embed   = embed.Build();
            });

            await Context.Message.DeleteAsync();

            await _autoRemoveMessageService.RegisterRemovableMessageAsync(message, Context.User);
        }