diff --git a/CHANGELOG.md b/CHANGELOG.md index 5c357a0f9..581080202 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,29 @@ Experimental changelog. Mostly based on [keepachangelog](https://keepachangelog.com/en/1.0.0/) except date format. a-c-f-r-o +## [4.3.18] - 26.12.2023 + +### Added + +- Added `.cacheusers` command (thx Kotz) +- Added `.clubreject` which lets you reject club applications + +### Changed + +- Updated discord lib, there should be less console errors now + +### Fixed + +- Fixed `icon_url` when using `.showembed` +- Fixed `.quoteshow` not showing sometimes (thx Cata) +- Notifications will no longer be sent if dms are off when using `.give` +- Users should no longer be able to apply to clubs while in a club already (especially not to the same club they're already in) + +### Removed + +- `.revimg` and `.revav` as google removed reverse image search +- + ## [4.3.17] - 06.09.2023 ### Fixed diff --git a/src/Nadeko.Bot.Common/DoAsUserMessage.cs b/src/Nadeko.Bot.Common/DoAsUserMessage.cs index 7ba2eb92e..40edc4234 100644 --- a/src/Nadeko.Bot.Common/DoAsUserMessage.cs +++ b/src/Nadeko.Bot.Common/DoAsUserMessage.cs @@ -49,6 +49,13 @@ public sealed class DoAsUserMessage : IUserMessage return _msg.RemoveAllReactionsForEmoteAsync(emote, options); } + public IAsyncEnumerable> GetReactionUsersAsync( + IEmote emoji, + int limit, + RequestOptions? options = null, + ReactionType type = ReactionType.Normal) + => _msg.GetReactionUsersAsync(emoji, limit, options, type); + public IAsyncEnumerable> GetReactionUsersAsync(IEmote emoji, int limit, RequestOptions? options = null) { @@ -137,5 +144,7 @@ public sealed class DoAsUserMessage : IUserMessage return _msg.Resolve(userHandling, channelHandling, roleHandling, everyoneHandling, emojiHandling); } + public MessageResolvedData ResolvedData => _msg.ResolvedData; + public IUserMessage ReferencedMessage => _msg.ReferencedMessage; } \ No newline at end of file diff --git a/src/Nadeko.Bot.Common/Services/Currency/CurrencyServiceExtensions.cs b/src/Nadeko.Bot.Common/Services/Currency/CurrencyServiceExtensions.cs index 603eb479b..e5dcfa7ba 100644 --- a/src/Nadeko.Bot.Common/Services/Currency/CurrencyServiceExtensions.cs +++ b/src/Nadeko.Bot.Common/Services/Currency/CurrencyServiceExtensions.cs @@ -1,4 +1,4 @@ -using NadekoBot.Services.Currency; +using NadekoBot.Services.Currency; namespace NadekoBot.Services; @@ -27,13 +27,19 @@ public static class CurrencyServiceExtensions if (await fromWallet.Transfer(amount, toWallet, extra)) { - await to.SendConfirmAsync(ebs, - string.IsNullOrWhiteSpace(note) - ? $"Received {formattedAmount} from {from} " - : $"Received {formattedAmount} from {from}: {note}"); + try + { + await to.SendConfirmAsync(ebs, + string.IsNullOrWhiteSpace(note) + ? $"Received {formattedAmount} from {from} " + : $"Received {formattedAmount} from {from}: {note}"); + } + catch + { + //ignored + } return true; } - return false; } -} \ No newline at end of file +} diff --git a/src/Nadeko.Bot.Common/SmartText/SmartTextEmbedAuthor.cs b/src/Nadeko.Bot.Common/SmartText/SmartTextEmbedAuthor.cs index 51e8369ea..5b243abe2 100644 --- a/src/Nadeko.Bot.Common/SmartText/SmartTextEmbedAuthor.cs +++ b/src/Nadeko.Bot.Common/SmartText/SmartTextEmbedAuthor.cs @@ -1,5 +1,6 @@ #nullable disable using Newtonsoft.Json; +using System.Text.Json.Serialization; namespace NadekoBot; @@ -8,6 +9,7 @@ public class SmartTextEmbedAuthor public string Name { get; set; } [JsonProperty("icon_url")] + [JsonPropertyName("icon_url")] public string IconUrl { get; set; } public string Url { get; set; } diff --git a/src/Nadeko.Bot.Common/SmartText/SmartTextEmbedFooter.cs b/src/Nadeko.Bot.Common/SmartText/SmartTextEmbedFooter.cs index 19489881f..54f9ed0dd 100644 --- a/src/Nadeko.Bot.Common/SmartText/SmartTextEmbedFooter.cs +++ b/src/Nadeko.Bot.Common/SmartText/SmartTextEmbedFooter.cs @@ -1,5 +1,6 @@ #nullable disable using Newtonsoft.Json; +using System.Text.Json.Serialization; namespace NadekoBot; @@ -8,5 +9,6 @@ public class SmartTextEmbedFooter public string Text { get; set; } [JsonProperty("icon_url")] + [JsonPropertyName("icon_url")] public string IconUrl { get; set; } } \ No newline at end of file diff --git a/src/Nadeko.Bot.Db/Extensions/DiscordUserExtensions.cs b/src/Nadeko.Bot.Db/Extensions/DiscordUserExtensions.cs index 3f15e189a..a71c02730 100644 --- a/src/Nadeko.Bot.Db/Extensions/DiscordUserExtensions.cs +++ b/src/Nadeko.Bot.Db/Extensions/DiscordUserExtensions.cs @@ -8,6 +8,54 @@ namespace NadekoBot.Db; public static class DiscordUserExtensions { + /// + /// Adds the specified to the database. If a database user with placeholder name + /// and discriminator is present in , their name and discriminator get updated accordingly. + /// + /// This database context. + /// The users to add or update in the database. + /// A tuple with the amount of new users added and old users updated. + public static async Task<(long UsersAdded, long UsersUpdated)> RefreshUsersAsync(this NadekoContext ctx, List users) + { + var presentDbUsers = await ctx.DiscordUser + .Select(x => new { x.UserId, x.Username, x.Discriminator }) + .Where(x => users.Select(y => y.Id).Contains(x.UserId)) + .ToArrayAsyncEF(); + + var usersToAdd = users + .Where(x => !presentDbUsers.Select(x => x.UserId).Contains(x.Id)) + .Select(x => new DiscordUser() + { + UserId = x.Id, + AvatarId = x.AvatarId, + Username = x.Username, + Discriminator = x.Discriminator + }); + + var added = (await ctx.BulkCopyAsync(usersToAdd)).RowsCopied; + var toUpdateUserIds = presentDbUsers + .Where(x => x.Username == "Unknown" && x.Discriminator == "????") + .Select(x => x.UserId) + .ToArray(); + + foreach (var user in users.Where(x => toUpdateUserIds.Contains(x.Id))) + { + await ctx.DiscordUser + .Where(x => x.UserId == user.Id) + .UpdateAsync(x => new DiscordUser() + { + Username = user.Username, + Discriminator = user.Discriminator, + + // .award tends to set AvatarId and DateAdded to NULL, so account for that. + AvatarId = user.AvatarId, + DateAdded = x.DateAdded ?? DateTime.UtcNow + }); + } + + return (added, toUpdateUserIds.Length); + } + public static Task GetByUserIdAsync( this IQueryable set, ulong userId) diff --git a/src/Nadeko.Bot.Modules.Administration/Self/SelfCommands.cs b/src/Nadeko.Bot.Modules.Administration/Self/SelfCommands.cs index 17003a869..e2573b423 100644 --- a/src/Nadeko.Bot.Modules.Administration/Self/SelfCommands.cs +++ b/src/Nadeko.Bot.Modules.Administration/Self/SelfCommands.cs @@ -1,4 +1,5 @@ #nullable disable +using NadekoBot.Db; using NadekoBot.Modules.Administration.Services; using Nadeko.Bot.Db.Models; using Nadeko.Common.Medusa; @@ -22,19 +23,53 @@ public partial class Administration private readonly IBotStrings _strings; private readonly IMedusaLoaderService _medusaLoader; private readonly ICoordinator _coord; + private readonly DbService _db; public SelfCommands( DiscordSocketClient client, + DbService db, IBotStrings strings, ICoordinator coord, IMedusaLoaderService medusaLoader) { _client = client; + _db = db; _strings = strings; _coord = coord; _medusaLoader = medusaLoader; } + + [Cmd] + [RequireContext(ContextType.Guild)] + [OwnerOnly] + public Task CacheUsers() + => CacheUsers(ctx.Guild); + + [Cmd] + [OwnerOnly] + public async Task CacheUsers(IGuild guild) + { + var downloadUsersTask = guild.DownloadUsersAsync(); + var message = await ReplyPendingLocalizedAsync(strs.cache_users_pending); + using var dbContext = _db.GetDbContext(); + + await downloadUsersTask; + + var users = (await guild.GetUsersAsync(CacheMode.CacheOnly)) + .Cast() + .ToList(); + + var (added, updated) = await dbContext.RefreshUsersAsync(users); + + await message.ModifyAsync(x => + x.Embed = _eb.Create() + .WithDescription(GetText(strs.cache_users_done(added, updated))) + .WithOkColor() + .Build() + ); + } + [Cmd] [OwnerOnly] public async Task DoAs(IUser user, [Leftover] string message) diff --git a/src/Nadeko.Bot.Modules.Searches/Searches.cs b/src/Nadeko.Bot.Modules.Searches/Searches.cs index 37b086740..659a7ea6a 100644 --- a/src/Nadeko.Bot.Modules.Searches/Searches.cs +++ b/src/Nadeko.Bot.Modules.Searches/Searches.cs @@ -421,30 +421,6 @@ public partial class Searches : NadekoModule await SendConfirmAsync("🐈" + GetText(strs.catfact), fact); } - //done in 3.0 - [Cmd] - [RequireContext(ContextType.Guild)] - public async Task Revav([Leftover] IGuildUser usr = null) - { - if (usr is null) - usr = (IGuildUser)ctx.User; - - var av = usr.RealAvatarUrl(); - await SendConfirmAsync($"https://images.google.com/searchbyimage?image_url={av}"); - } - - //done in 3.0 - [Cmd] - public async Task Revimg([Leftover] string imageLink = null) - { - imageLink = imageLink?.Trim() ?? ""; - - if (string.IsNullOrWhiteSpace(imageLink)) - return; - - await SendConfirmAsync($"https://images.google.com/searchbyimage?image_url={imageLink}"); - } - [Cmd] public async Task Wiki([Leftover] string query = null) { diff --git a/src/Nadeko.Bot.Modules.Searches/SearchesService.cs b/src/Nadeko.Bot.Modules.Searches/SearchesService.cs index 984e5f7f9..7fc964051 100644 --- a/src/Nadeko.Bot.Modules.Searches/SearchesService.cs +++ b/src/Nadeko.Bot.Modules.Searches/SearchesService.cs @@ -8,6 +8,9 @@ using SixLabors.ImageSharp; using SixLabors.ImageSharp.Drawing.Processing; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; +using System.Collections; +using System.Net.Http.Json; +using System.Text.Json.Serialization; using Color = SixLabors.ImageSharp.Color; using Image = SixLabors.ImageSharp.Image; diff --git a/src/Nadeko.Bot.Modules.Utility/Quote/QuoteCommands.cs b/src/Nadeko.Bot.Modules.Utility/Quote/QuoteCommands.cs index fa7893404..973a9705d 100644 --- a/src/Nadeko.Bot.Modules.Utility/Quote/QuoteCommands.cs +++ b/src/Nadeko.Bot.Modules.Utility/Quote/QuoteCommands.cs @@ -129,15 +129,27 @@ public partial class Utility } private async Task ShowQuoteData(Quote data) - => await ctx.Channel.EmbedAsync(_eb.Create(ctx) - .WithOkColor() - .WithTitle(GetText(strs.quote_id($"#{data.Id}"))) - .AddField(GetText(strs.trigger), data.Keyword) - .AddField(GetText(strs.response), - Format.Sanitize(data.Text).Replace("](", "]\\(")) - .WithFooter( - GetText(strs.created_by($"{data.AuthorName} ({data.AuthorId})")))); + { + var eb = _eb.Create(ctx) + .WithOkColor() + .WithTitle($"{GetText(strs.quote_id($"#{data.Id}"))} | {GetText(strs.response)}:") + .WithDescription(Format.Sanitize(data.Text).Replace("](", "]\\(").TrimTo(4096)) + .AddField(GetText(strs.trigger), data.Keyword) + .WithFooter( + GetText(strs.created_by($"{data.AuthorName} ({data.AuthorId})"))) + .Build(); + if (!(data.Text.Length > 4096)) + { + await ctx.Channel.SendMessageAsync(embed: eb); + return; + } + + await ctx.Channel.SendFileAsync( + attachment: new FileAttachment(await data.Text.ToStream(), "quote.txt"), + embed: eb); + } + private async Task QuoteSearchinternalAsync(string? keyword, string textOrAuthor) { if (string.IsNullOrWhiteSpace(textOrAuthor)) diff --git a/src/Nadeko.Bot.Modules.Utility/Utility.cs b/src/Nadeko.Bot.Modules.Utility/Utility.cs index e1dd37b4c..f24184cc2 100644 --- a/src/Nadeko.Bot.Modules.Utility/Utility.cs +++ b/src/Nadeko.Bot.Modules.Utility/Utility.cs @@ -1,4 +1,6 @@ #nullable disable +using Microsoft.CodeAnalysis.CSharp.Scripting; +using Microsoft.CodeAnalysis.Scripting; using NadekoBot.Modules.Utility.Services; using Newtonsoft.Json; using System.Diagnostics; @@ -461,13 +463,14 @@ public partial class Utility : NadekoModule { if (tags.Length == 0) tags = new[] { name }; - - await ctx.Guild.CreateStickerAsync(name, + + await ctx.Guild.CreateStickerAsync( + name, stream, $"{name}.{format}", tags, - description: string.IsNullOrWhiteSpace(description) ? "Missing description" : description - ); + string.IsNullOrWhiteSpace(description) ? "Missing description" : description + ); await ctx.OkAsync(); } diff --git a/src/NadekoBot/NadekoBot.csproj b/src/NadekoBot/NadekoBot.csproj index d8801158c..e3a1c2700 100644 --- a/src/NadekoBot/NadekoBot.csproj +++ b/src/NadekoBot/NadekoBot.csproj @@ -27,6 +27,7 @@ + diff --git a/src/NadekoBot/data/aliases.yml b/src/NadekoBot/data/aliases.yml index 489ae6721..e372fa6d2 100644 --- a/src/NadekoBot/data/aliases.yml +++ b/src/NadekoBot/data/aliases.yml @@ -647,10 +647,6 @@ chucknorris: magicitem: - magicitem - mi -revav: - - revav -revimg: - - revimg safebooru: - safebooru wiki: @@ -1393,4 +1389,6 @@ autopublish: - autopublish doas: - doas - - execas \ No newline at end of file + - execas +cacheusers: + - cacheusers \ No newline at end of file diff --git a/src/NadekoBot/data/strings/commands/commands.en-US.yml b/src/NadekoBot/data/strings/commands/commands.en-US.yml index ec23dd5a6..a4fda2de5 100644 --- a/src/NadekoBot/data/strings/commands/commands.en-US.yml +++ b/src/NadekoBot/data/strings/commands/commands.en-US.yml @@ -2371,4 +2371,9 @@ doas: clubrename: desc: "Renames your club. Requires you club ownership or club-admin status." args: - - "New cool club name" \ No newline at end of file + - "New cool club name" +cacheusers: + desc: Caches users of a Discord server and saves them to the database. + args: + - "" + - "serverId" \ No newline at end of file diff --git a/src/NadekoBot/data/strings/responses/responses.en-US.json b/src/NadekoBot/data/strings/responses/responses.en-US.json index a3f4d4d4d..f143a5ec3 100644 --- a/src/NadekoBot/data/strings/responses/responses.en-US.json +++ b/src/NadekoBot/data/strings/responses/responses.en-US.json @@ -1060,5 +1060,7 @@ "sticker_missing_name": "Please specify a name for the sticker.", "thread_deleted": "Thread Deleted", "thread_created": "Thread Created", - "supported_languages": "Supported Languages" + "supported_languages": "Supported Languages", + "cache_users_pending": "Updating users, please wait...", + "cache_users_done": "{0} users were added and {1} users were updated." }