From c78296fd2d5924bb2b43b5c549286e5c2b9a65b2 Mon Sep 17 00:00:00 2001 From: Mihail Gribkov <61027276+Misha-133@users.noreply.github.com> Date: Wed, 4 Dec 2024 23:49:08 +0300 Subject: [PATCH] [Fix] NRE in message commands in user app contexts (#3035) * bump few more things * expose `MentionedRoleIds` on `SocketMessage` + add some syntax sugar * right, we can't have nice things cuz netfx * omfg that's an I/O operation in ctor bruh --- .../Discord.Net.Commands.csproj | 2 +- .../Entities/Messages/SocketMessage.cs | 25 +++++++----- .../Entities/Messages/SocketUserMessage.cs | 40 +++++++++++-------- .../Discord.Net.Tests.Integration.csproj | 4 +- .../Discord.Net.Tests.Unit.csproj | 4 +- 5 files changed, 45 insertions(+), 30 deletions(-) diff --git a/src/Discord.Net.Commands/Discord.Net.Commands.csproj b/src/Discord.Net.Commands/Discord.Net.Commands.csproj index 51f1d415a7..5a6e51591e 100644 --- a/src/Discord.Net.Commands/Discord.Net.Commands.csproj +++ b/src/Discord.Net.Commands/Discord.Net.Commands.csproj @@ -20,6 +20,6 @@ - + diff --git a/src/Discord.Net.WebSocket/Entities/Messages/SocketMessage.cs b/src/Discord.Net.WebSocket/Entities/Messages/SocketMessage.cs index c778137e01..288a910889 100644 --- a/src/Discord.Net.WebSocket/Entities/Messages/SocketMessage.cs +++ b/src/Discord.Net.WebSocket/Entities/Messages/SocketMessage.cs @@ -16,8 +16,8 @@ public abstract class SocketMessage : SocketEntity, IMessage { #region SocketMessage private long _timestampTicks; - private readonly List _reactions = new List(); - private ImmutableArray _userMentions = ImmutableArray.Create(); + private readonly List _reactions = []; + private ImmutableArray _userMentions = []; /// /// Gets the author of this message. @@ -113,18 +113,26 @@ public abstract class SocketMessage : SocketEntity, IMessage /// /// Collection of WebSocket-based guild channels. /// - public virtual IReadOnlyCollection MentionedChannels => ImmutableArray.Create(); + public virtual IReadOnlyCollection MentionedChannels => []; /// /// Returns the roles mentioned in this message. /// + /// + /// This collection may be missing values due to the guild being missing from cache (i.e. in user app interaction context). + /// In that case you can use the property. + /// /// /// Collection of WebSocket-based roles. /// - public virtual IReadOnlyCollection MentionedRoles => ImmutableArray.Create(); + public virtual IReadOnlyCollection MentionedRoles => []; + + /// + public virtual IReadOnlyCollection MentionedRoleIds => []; + /// - public virtual IReadOnlyCollection Tags => ImmutableArray.Create(); + public virtual IReadOnlyCollection Tags => []; /// - public virtual IReadOnlyCollection Stickers => ImmutableArray.Create(); + public virtual IReadOnlyCollection Stickers => []; /// public IReadOnlyDictionary Reactions => _reactions.GroupBy(r => r.Emote).ToDictionary(x => x.Key, x => new ReactionMetadata { ReactionCount = x.Count(), IsMe = x.Any(y => y.UserId == Discord.CurrentUser.Id) }); /// @@ -271,7 +279,8 @@ internal virtual void Update(ClientState state, Model model) var val = value[i]; if (val != null) { - var user = Channel.GetUserAsync(val.Id, CacheMode.CacheOnly).GetAwaiter().GetResult() as SocketUser; + // TODO: this is cursed af and should be yeeted + var user = Channel?.GetUserAsync(val.Id, CacheMode.CacheOnly).GetAwaiter().GetResult() as SocketUser; if (user != null) newMentions.Add(user); else @@ -346,8 +355,6 @@ public Task DeleteAsync(RequestOptions options = null) /// IReadOnlyCollection IMessage.MentionedChannelIds => MentionedChannels.Select(x => x.Id).ToImmutableArray(); /// - IReadOnlyCollection IMessage.MentionedRoleIds => MentionedRoles.Select(x => x.Id).ToImmutableArray(); - /// IReadOnlyCollection IMessage.MentionedUserIds => MentionedUsers.Select(x => x.Id).ToImmutableArray(); /// diff --git a/src/Discord.Net.WebSocket/Entities/Messages/SocketUserMessage.cs b/src/Discord.Net.WebSocket/Entities/Messages/SocketUserMessage.cs index dc86503c54..31df13df20 100644 --- a/src/Discord.Net.WebSocket/Entities/Messages/SocketUserMessage.cs +++ b/src/Discord.Net.WebSocket/Entities/Messages/SocketUserMessage.cs @@ -18,11 +18,12 @@ public class SocketUserMessage : SocketMessage, IUserMessage private bool _isMentioningEveryone, _isTTS, _isPinned; private long? _editedTimestampTicks; private IUserMessage _referencedMessage; - private ImmutableArray _attachments = ImmutableArray.Create(); - private ImmutableArray _embeds = ImmutableArray.Create(); - private ImmutableArray _tags = ImmutableArray.Create(); - private ImmutableArray _roleMentions = ImmutableArray.Create(); - private ImmutableArray _stickers = ImmutableArray.Create(); + private ImmutableArray _attachments = []; + private ImmutableArray _embeds = []; + private ImmutableArray _tags = []; + private ImmutableArray _roleMentions; + private ImmutableArray _roleMentionsIds = []; + private ImmutableArray _stickers = []; /// public override bool IsTTS => _isTTS; @@ -44,6 +45,9 @@ public class SocketUserMessage : SocketMessage, IUserMessage public override IReadOnlyCollection MentionedChannels => MessageHelper.FilterTagsByValue(TagType.ChannelMention, _tags); /// public override IReadOnlyCollection MentionedRoles => _roleMentions; + /// + public override IReadOnlyCollection MentionedRoleIds => _roleMentionsIds; + /// public override IReadOnlyCollection Stickers => _stickers; /// @@ -64,6 +68,7 @@ public class SocketUserMessage : SocketMessage, IUserMessage internal SocketUserMessage(DiscordSocketClient discord, ulong id, ISocketMessageChannel channel, SocketUser author, MessageSource source) : base(discord, id, channel, author, source) { + _roleMentions = ImmutableArray.Create(); } internal new static SocketUserMessage Create(DiscordSocketClient discord, ClientState state, SocketUser author, ISocketMessageChannel channel, Model model) { @@ -87,7 +92,12 @@ internal override void Update(ClientState state, Model model) if (model.MentionEveryone.IsSpecified) _isMentioningEveryone = model.MentionEveryone.Value; if (model.RoleMentions.IsSpecified) - _roleMentions = model.RoleMentions.Value.Select(x => guild.GetRole(x)).ToImmutableArray(); + { + if (guild is not null) + _roleMentions = model.RoleMentions.Value.Select(x => guild.GetRole(x)).ToImmutableArray(); + _roleMentionsIds = model.RoleMentions.Value.ToImmutableArray(); + } + if (model.Attachments.IsSpecified) { @@ -100,7 +110,7 @@ internal override void Update(ClientState state, Model model) _attachments = attachments.ToImmutable(); } else - _attachments = ImmutableArray.Create(); + _attachments = []; } if (model.Embeds.IsSpecified) @@ -114,7 +124,7 @@ internal override void Update(ClientState state, Model model) _embeds = embeds.ToImmutable(); } else - _embeds = ImmutableArray.Create(); + _embeds = []; } if (model.Content.IsSpecified) @@ -155,26 +165,24 @@ internal override void Update(ClientState state, Model model) if (value.Length > 0) { var stickers = ImmutableArray.CreateBuilder(value.Length); - for (int i = 0; i < value.Length; i++) + foreach (var stickerItem in value) { - var stickerItem = value[i]; SocketSticker sticker = null; if (guild != null) sticker = guild.GetSticker(stickerItem.Id); - if (sticker == null) - sticker = Discord.GetSticker(stickerItem.Id); + sticker ??= Discord.GetSticker(stickerItem.Id); // if they want to auto resolve if (Discord.AlwaysResolveStickers) { - sticker = Task.Run(async () => await Discord.GetStickerAsync(stickerItem.Id).ConfigureAwait(false)).GetAwaiter().GetResult(); + var item = stickerItem; + sticker = Task.Run(async () => await Discord.GetStickerAsync(item.Id).ConfigureAwait(false)).GetAwaiter().GetResult(); } // if its still null, create an unknown - if (sticker == null) - sticker = SocketUnknownSticker.Create(Discord, stickerItem); + sticker ??= SocketUnknownSticker.Create(Discord, stickerItem); stickers.Add(sticker); } @@ -182,7 +190,7 @@ internal override void Update(ClientState state, Model model) _stickers = stickers.ToImmutable(); } else - _stickers = ImmutableArray.Create(); + _stickers = []; } if (model.Resolved.IsSpecified) diff --git a/test/Discord.Net.Tests.Integration/Discord.Net.Tests.Integration.csproj b/test/Discord.Net.Tests.Integration/Discord.Net.Tests.Integration.csproj index 5b34406ab8..5e6340153e 100644 --- a/test/Discord.Net.Tests.Integration/Discord.Net.Tests.Integration.csproj +++ b/test/Discord.Net.Tests.Integration/Discord.Net.Tests.Integration.csproj @@ -14,8 +14,8 @@ - - + + all diff --git a/test/Discord.Net.Tests.Unit/Discord.Net.Tests.Unit.csproj b/test/Discord.Net.Tests.Unit/Discord.Net.Tests.Unit.csproj index e7419d09bd..c028a79bfe 100644 --- a/test/Discord.Net.Tests.Unit/Discord.Net.Tests.Unit.csproj +++ b/test/Discord.Net.Tests.Unit/Discord.Net.Tests.Unit.csproj @@ -12,8 +12,8 @@ - - + +