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)); }
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(); }
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(); }
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())); }
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(); }
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 { })
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(); }
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(); }
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(); }
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); }