From ae45329d2b9d49e9c29b9489e9c579969b66e9e9 Mon Sep 17 00:00:00 2001 From: Cata Date: Fri, 17 May 2024 11:29:51 +0000 Subject: [PATCH] add: setserverbanner and setservericon commands --- .../Modules/Administration/Administration.cs | 89 +++++++++++++++++++ .../Administration/AdministrationService.cs | 47 +++++++++- .../Administration/Self/SelfCommands.cs | 37 +------- .../_common/SetServerBannerResult.cs | 9 ++ .../_common/SetServerIconResult.cs | 8 ++ .../Modules/Utility/Quote/QuoteCommands.cs | 2 +- src/NadekoBot/data/aliases.yml | 6 ++ .../data/strings/commands/commands.en-US.yml | 14 +++ .../strings/responses/responses.en-US.json | 7 +- 9 files changed, 180 insertions(+), 39 deletions(-) create mode 100644 src/NadekoBot/Modules/Administration/_common/SetServerBannerResult.cs create mode 100644 src/NadekoBot/Modules/Administration/_common/SetServerIconResult.cs diff --git a/src/NadekoBot/Modules/Administration/Administration.cs b/src/NadekoBot/Modules/Administration/Administration.cs index 8dc3d20b4..d5133b4a1 100644 --- a/src/NadekoBot/Modules/Administration/Administration.cs +++ b/src/NadekoBot/Modules/Administration/Administration.cs @@ -1,5 +1,6 @@ #nullable disable using NadekoBot.Common.TypeReaders.Models; +using NadekoBot.Modules.Administration._common.results; using NadekoBot.Modules.Administration.Services; namespace NadekoBot.Modules.Administration; @@ -405,4 +406,92 @@ public partial class Administration : NadekoModule await Response().Confirm(strs.autopublish_disable).SendAsync(); } } + + [Cmd] + [UserPerm(GuildPerm.ManageNicknames)] + [BotPerm(GuildPerm.ChangeNickname)] + [Priority(0)] + public async Task SetNick([Leftover] string newNick = null) + { + if (string.IsNullOrWhiteSpace(newNick)) + return; + var curUser = await ctx.Guild.GetCurrentUserAsync(); + await curUser.ModifyAsync(u => u.Nickname = newNick); + + await Response().Confirm(strs.bot_nick(Format.Bold(newNick) ?? "-")).SendAsync(); + } + + [Cmd] + [BotPerm(GuildPerm.ManageNicknames)] + [UserPerm(GuildPerm.ManageNicknames)] + [Priority(1)] + public async Task SetNick(IGuildUser gu, [Leftover] string newNick = null) + { + var sg = (SocketGuild)ctx.Guild; + if (sg.OwnerId == gu.Id + || gu.GetRoles().Max(r => r.Position) >= sg.CurrentUser.GetRoles().Max(r => r.Position)) + { + await Response().Error(strs.insuf_perms_i).SendAsync(); + return; + } + + await gu.ModifyAsync(u => u.Nickname = newNick); + + await Response() + .Confirm(strs.user_nick(Format.Bold(gu.ToString()), Format.Bold(newNick) ?? "-")) + .SendAsync(); + } + + + [Cmd] + [RequireContext(ContextType.Guild)] + [UserPerm(GuildPermission.ManageGuild)] + public async Task SetServerBanner([Leftover] string img = null) + { + // Tier2 or higher is required to set a banner. + if (ctx.Guild.PremiumTier is PremiumTier.Tier1 or PremiumTier.None) return; + + var result = await _service.SetServerBannerAsync(ctx.Guild, img); + + switch (result) + { + case SetServerBannerResult.Success: + await Response().Confirm(strs.set_srvr_banner).SendAsync(); + break; + case SetServerBannerResult.InvalidFileType: + await Response().Error(strs.srvr_banner_invalid).SendAsync(); + break; + case SetServerBannerResult.Toolarge: + await Response().Error(strs.srvr_banner_too_large).SendAsync(); + break; + case SetServerBannerResult.InvalidURL: + await Response().Error(strs.srvr_banner_invalid_url).SendAsync(); + break; + default: + throw new ArgumentOutOfRangeException(); + } + } + + [Cmd] + [RequireContext(ContextType.Guild)] + [UserPerm(GuildPermission.ManageGuild)] + public async Task SetServerIcon([Leftover] string img = null) + { + var result = await _service.SetServerIconAsync(ctx.Guild, img); + + switch (result) + { + case SetServerIconResult.Success: + await Response().Confirm(strs.set_srvr_icon).SendAsync(); + break; + case SetServerIconResult.InvalidFileType: + await Response().Error(strs.srvr_banner_invalid).SendAsync(); + break; + case SetServerIconResult.InvalidURL: + await Response().Error(strs.srvr_banner_invalid_url).SendAsync(); + break; + default: + throw new ArgumentOutOfRangeException(); + } + } } \ No newline at end of file diff --git a/src/NadekoBot/Modules/Administration/AdministrationService.cs b/src/NadekoBot/Modules/Administration/AdministrationService.cs index d28e40978..6bd81721b 100644 --- a/src/NadekoBot/Modules/Administration/AdministrationService.cs +++ b/src/NadekoBot/Modules/Administration/AdministrationService.cs @@ -2,6 +2,7 @@ using Microsoft.EntityFrameworkCore; using NadekoBot.Db; using NadekoBot.Db.Models; +using NadekoBot.Modules.Administration._common.results; namespace NadekoBot.Modules.Administration.Services; @@ -13,17 +14,20 @@ public class AdministrationService : INService private readonly DbService _db; private readonly IReplacementService _repSvc; private readonly ILogCommandService _logService; + private readonly IHttpClientFactory _httpFactory; public AdministrationService( IBot bot, CommandHandler cmdHandler, DbService db, IReplacementService repSvc, - ILogCommandService logService) + ILogCommandService logService, + IHttpClientFactory factory) { _db = db; _repSvc = repSvc; _logService = logService; + _httpFactory = factory; DeleteMessagesOnCommand = new(bot.AllGuildConfigs.Where(g => g.DeleteMessageOnCommand).Select(g => g.GuildId)); @@ -158,4 +162,45 @@ public class AdministrationService : INService await umsg.EditAsync(text); } + + public async Task SetServerBannerAsync(IGuild guild, string img) + { + if (!IsValidUri(img)) return SetServerBannerResult.InvalidURL; + + var uri = new Uri(img); + + using var http = _httpFactory.CreateClient(); + using var sr = await http.GetAsync(uri, HttpCompletionOption.ResponseHeadersRead); + + if (!sr.IsImage()) return SetServerBannerResult.InvalidFileType; + + if (sr.GetContentLength() > 8.Megabytes()) + { + return SetServerBannerResult.Toolarge; + } + + await using var imageStream = await sr.Content.ReadAsStreamAsync(); + + await guild.ModifyAsync(x => x.Banner = new Image(imageStream)); + return SetServerBannerResult.Success; + } + + public async Task SetServerIconAsync(IGuild guild, string img) + { + if (!IsValidUri(img)) return SetServerIconResult.InvalidURL; + + var uri = new Uri(img); + + using var http = _httpFactory.CreateClient(); + using var sr = await http.GetAsync(uri, HttpCompletionOption.ResponseHeadersRead); + + if (!sr.IsImage()) return SetServerIconResult.InvalidFileType; + + await using var imageStream = await sr.Content.ReadAsStreamAsync(); + + await guild.ModifyAsync(x => x.Icon = new Image(imageStream)); + return SetServerIconResult.Success; + } + + private bool IsValidUri(string img) => !string.IsNullOrWhiteSpace(img) && Uri.IsWellFormedUriString(img, UriKind.Absolute); } \ No newline at end of file diff --git a/src/NadekoBot/Modules/Administration/Self/SelfCommands.cs b/src/NadekoBot/Modules/Administration/Self/SelfCommands.cs index 070aa4de7..0ee308398 100644 --- a/src/NadekoBot/Modules/Administration/Self/SelfCommands.cs +++ b/src/NadekoBot/Modules/Administration/Self/SelfCommands.cs @@ -459,42 +459,7 @@ public partial class Administration await Response().Confirm(strs.bot_name(Format.Bold(newName))).SendAsync(); } - - [Cmd] - [UserPerm(GuildPerm.ManageNicknames)] - [BotPerm(GuildPerm.ChangeNickname)] - [Priority(0)] - public async Task SetNick([Leftover] string newNick = null) - { - if (string.IsNullOrWhiteSpace(newNick)) - return; - var curUser = await ctx.Guild.GetCurrentUserAsync(); - await curUser.ModifyAsync(u => u.Nickname = newNick); - - await Response().Confirm(strs.bot_nick(Format.Bold(newNick) ?? "-")).SendAsync(); - } - - [Cmd] - [BotPerm(GuildPerm.ManageNicknames)] - [UserPerm(GuildPerm.ManageNicknames)] - [Priority(1)] - public async Task SetNick(IGuildUser gu, [Leftover] string newNick = null) - { - var sg = (SocketGuild)ctx.Guild; - if (sg.OwnerId == gu.Id - || gu.GetRoles().Max(r => r.Position) >= sg.CurrentUser.GetRoles().Max(r => r.Position)) - { - await Response().Error(strs.insuf_perms_i).SendAsync(); - return; - } - - await gu.ModifyAsync(u => u.Nickname = newNick); - - await Response() - .Confirm(strs.user_nick(Format.Bold(gu.ToString()), Format.Bold(newNick) ?? "-")) - .SendAsync(); - } - + [Cmd] [OwnerOnly] public async Task SetStatus([Leftover] SettableUserStatus status) diff --git a/src/NadekoBot/Modules/Administration/_common/SetServerBannerResult.cs b/src/NadekoBot/Modules/Administration/_common/SetServerBannerResult.cs new file mode 100644 index 000000000..b1163369c --- /dev/null +++ b/src/NadekoBot/Modules/Administration/_common/SetServerBannerResult.cs @@ -0,0 +1,9 @@ +namespace NadekoBot.Modules.Administration._common.results; + +public enum SetServerBannerResult +{ + Success, + InvalidFileType, + Toolarge, + InvalidURL +} diff --git a/src/NadekoBot/Modules/Administration/_common/SetServerIconResult.cs b/src/NadekoBot/Modules/Administration/_common/SetServerIconResult.cs new file mode 100644 index 000000000..72547b9f0 --- /dev/null +++ b/src/NadekoBot/Modules/Administration/_common/SetServerIconResult.cs @@ -0,0 +1,8 @@ +namespace NadekoBot.Modules.Administration._common.results; + +public enum SetServerIconResult +{ + Success, + InvalidFileType, + InvalidURL +} \ No newline at end of file diff --git a/src/NadekoBot/Modules/Utility/Quote/QuoteCommands.cs b/src/NadekoBot/Modules/Utility/Quote/QuoteCommands.cs index fbc4b5323..8095268a1 100644 --- a/src/NadekoBot/Modules/Utility/Quote/QuoteCommands.cs +++ b/src/NadekoBot/Modules/Utility/Quote/QuoteCommands.cs @@ -1,4 +1,4 @@ -#nullable disable warnings +#nullable disable warnings using NadekoBot.Common.Yml; using NadekoBot.Db; using NadekoBot.Db.Models; diff --git a/src/NadekoBot/data/aliases.yml b/src/NadekoBot/data/aliases.yml index e36ce1ef4..af2cf1565 100644 --- a/src/NadekoBot/data/aliases.yml +++ b/src/NadekoBot/data/aliases.yml @@ -195,6 +195,12 @@ setname: - newnm setnick: - setnick +setserverbanner: + - setserverbanner + - serverbanner +setservericon: + - setservericon + - servericon setavatar: - setavatar - setav diff --git a/src/NadekoBot/data/strings/commands/commands.en-US.yml b/src/NadekoBot/data/strings/commands/commands.en-US.yml index d61543e48..d7e734f3e 100644 --- a/src/NadekoBot/data/strings/commands/commands.en-US.yml +++ b/src/NadekoBot/data/strings/commands/commands.en-US.yml @@ -809,6 +809,20 @@ setgame: desc: "The activity type determines whether the bot is engaged in a game, listening to audio, or watching a video." game: desc: "The current state of the bot's activity in the game." +setserverbanner: + desc: Sets a new banner image for the current server. Parameter is a direct link to an image. + ex: + - https://i.imgur.com/xTG3a1I.jpg + params: + - img: + desc: "The URL of the image file to be displayed as the bot's banner." +setservericon: + desc: Sets a new icon image for the current server. Parameter is a direct link to an image. + ex: + - https://i.imgur.com/xTG3a1I.jpg + params: + - img: + desc: "The URL of the image file to be displayed as the bot's banner." send: desc: 'Sends a message to a channel or user. Channel or user can be ' ex: diff --git a/src/NadekoBot/data/strings/responses/responses.en-US.json b/src/NadekoBot/data/strings/responses/responses.en-US.json index 03d7b707f..c7e8e8b5e 100644 --- a/src/NadekoBot/data/strings/responses/responses.en-US.json +++ b/src/NadekoBot/data/strings/responses/responses.en-US.json @@ -188,6 +188,11 @@ "setrole_err": "Failed to add role. I have insufficient permissions.", "set_avatar": "New avatar set!", "set_banner": "New banner set!", + "set_srvr_icon": "New server icon set!", + "set_srvr_banner": "New server banner set!", + "srvr_banner_invalid": "Specified image has an invalid filetype. Make sure you're specifying a direct image url.", + "srvr_banner_too_large": "Specified image is too large! Maximum size is 8MB.", + "srvr_banner_invalid_url": "Specified url is not valid. Make sure you're specifying a direct image url.", "set_channel_name": "New channel name set.", "set_game": "New game set!", "set_stream": "New stream set!", @@ -875,7 +880,7 @@ "club_disband_error": "Error. You are either not in a club, or you are not the owner of your club.", "club_icon_too_large": "Image is too large.", "club_icon_invalid_filetype": "Specified image has an invalid filetype. Make sure you're specifying a direct image url.", - "club_icon_url_format": "You must specify an absolute image url/.", + "club_icon_url_format": "You must specify an absolute image url.", "club_icon_set": "New club icon set.", "club_bans_for": "Bans for {0} club", "club_apps_for": "Applicants for {0} club",