using Discord.Rest; using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; using System.Threading.Tasks; using Model = Discord.API.Message; namespace Discord.WebSocket { /// /// Represents a WebSocket-based message. /// public abstract class SocketMessage : SocketEntity, IMessage { private long _timestampTicks; private readonly List _reactions = new List(); /// /// Gets the author of this message. /// /// /// A WebSocket-based user object. /// public SocketUser Author { get; } /// /// Gets the source channel of the message. /// /// /// A WebSocket-based message channel. /// public ISocketMessageChannel Channel { get; } /// public MessageSource Source { get; } /// public string Content { get; private set; } /// public DateTimeOffset CreatedAt => SnowflakeUtils.FromSnowflake(Id); /// public virtual bool IsTTS => false; /// public virtual bool IsPinned => false; /// public virtual bool IsSuppressed => false; /// public virtual DateTimeOffset? EditedTimestamp => null; /// public MessageActivity Activity { get; private set; } /// public MessageApplication Application { get; private set; } /// public MessageReference Reference { get; private set; } /// /// Returns all attachments included in this message. /// /// /// Collection of attachments. /// public virtual IReadOnlyCollection Attachments => ImmutableArray.Create(); /// /// Returns all embeds included in this message. /// /// /// Collection of embed objects. /// public virtual IReadOnlyCollection Embeds => ImmutableArray.Create(); /// /// Returns the channels mentioned in this message. /// /// /// Collection of WebSocket-based guild channels. /// public virtual IReadOnlyCollection MentionedChannels => ImmutableArray.Create(); /// /// Returns the roles mentioned in this message. /// /// /// Collection of WebSocket-based roles. /// public virtual IReadOnlyCollection MentionedRoles => ImmutableArray.Create(); /// /// Returns the users mentioned in this message. /// /// /// Collection of WebSocket-based users. /// public virtual IReadOnlyCollection MentionedUsers => ImmutableArray.Create(); /// public virtual IReadOnlyCollection Tags => ImmutableArray.Create(); /// 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) }); /// public DateTimeOffset Timestamp => DateTimeUtils.FromTicks(_timestampTicks); internal SocketMessage(DiscordSocketClient discord, ulong id, ISocketMessageChannel channel, SocketUser author, MessageSource source) : base(discord, id) { Channel = channel; Author = author; Source = source; } internal static SocketMessage Create(DiscordSocketClient discord, ClientState state, SocketUser author, ISocketMessageChannel channel, Model model) { if (model.Type == MessageType.Default) return SocketUserMessage.Create(discord, state, author, channel, model); else return SocketSystemMessage.Create(discord, state, author, channel, model); } internal virtual void Update(ClientState state, Model model) { if (model.Timestamp.IsSpecified) _timestampTicks = model.Timestamp.Value.UtcTicks; if (model.Content.IsSpecified) Content = model.Content.Value; if (model.Application.IsSpecified) { // create a new Application from the API model Application = new MessageApplication() { Id = model.Application.Value.Id, CoverImage = model.Application.Value.CoverImage, Description = model.Application.Value.Description, Icon = model.Application.Value.Icon, Name = model.Application.Value.Name }; } if (model.Activity.IsSpecified) { // create a new Activity from the API model Activity = new MessageActivity() { Type = model.Activity.Value.Type.Value, PartyId = model.Activity.Value.PartyId.Value }; } if (model.Reference.IsSpecified) { // Creates a new Reference from the API model Reference = new MessageReference { GuildId = model.Reference.Value.GuildId, ChannelId = model.Reference.Value.ChannelId, MessageId = model.Reference.Value.MessageId }; } } /// public Task DeleteAsync(RequestOptions options = null) => MessageHelper.DeleteAsync(this, Discord, options); /// /// Gets the content of the message. /// /// /// Content of the message. /// public override string ToString() => Content; internal SocketMessage Clone() => MemberwiseClone() as SocketMessage; //IMessage /// IUser IMessage.Author => Author; /// IMessageChannel IMessage.Channel => Channel; /// MessageType IMessage.Type => MessageType.Default; /// IReadOnlyCollection IMessage.Attachments => Attachments; /// IReadOnlyCollection IMessage.Embeds => Embeds; /// 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(); internal void AddReaction(SocketReaction reaction) { _reactions.Add(reaction); } internal void RemoveReaction(SocketReaction reaction) { if (_reactions.Contains(reaction)) _reactions.Remove(reaction); } internal void ClearReactions() { _reactions.Clear(); } /// public Task AddReactionAsync(IEmote emote, RequestOptions options = null) => MessageHelper.AddReactionAsync(this, emote, Discord, options); /// public Task RemoveReactionAsync(IEmote emote, IUser user, RequestOptions options = null) => MessageHelper.RemoveReactionAsync(this, user.Id, emote, Discord, options); /// public Task RemoveReactionAsync(IEmote emote, ulong userId, RequestOptions options = null) => MessageHelper.RemoveReactionAsync(this, userId, emote, Discord, options); /// public Task RemoveAllReactionsAsync(RequestOptions options = null) => MessageHelper.RemoveAllReactionsAsync(this, Discord, options); /// public IAsyncEnumerable> GetReactionUsersAsync(IEmote emote, int limit, RequestOptions options = null) => MessageHelper.GetReactionUsersAsync(this, emote, limit, Discord, options); } }