using Discord.Audio; using Discord.Rest; using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; using System.Linq; using System.Threading.Tasks; using UserModel = Discord.API.User; using MemberModel = Discord.API.GuildMember; using PresenceModel = Discord.API.Presence; namespace Discord.WebSocket { /// /// Represents a WebSocket-based guild user. /// [DebuggerDisplay(@"{DebuggerDisplay,nq}")] public class SocketGuildUser : SocketUser, IGuildUser { private long? _premiumSinceTicks; private long? _joinedAtTicks; private ImmutableArray _roleIds; internal override SocketGlobalUser GlobalUser { get; } /// /// Gets the guild the user is in. /// public SocketGuild Guild { get; } /// public string Nickname { get; private set; } /// public override bool IsBot { get { return GlobalUser.IsBot; } internal set { GlobalUser.IsBot = value; } } /// public override string Username { get { return GlobalUser.Username; } internal set { GlobalUser.Username = value; } } /// public override ushort DiscriminatorValue { get { return GlobalUser.DiscriminatorValue; } internal set { GlobalUser.DiscriminatorValue = value; } } /// public override string AvatarId { get { return GlobalUser.AvatarId; } internal set { GlobalUser.AvatarId = value; } } /// public GuildPermissions GuildPermissions => new GuildPermissions(Permissions.ResolveGuild(Guild, this)); internal override SocketPresence Presence { get; set; } /// public override bool IsWebhook => false; /// public bool IsSelfDeafened => VoiceState?.IsSelfDeafened ?? false; /// public bool IsSelfMuted => VoiceState?.IsSelfMuted ?? false; /// public bool IsSuppressed => VoiceState?.IsSuppressed ?? false; /// public bool IsDeafened => VoiceState?.IsDeafened ?? false; /// public bool IsMuted => VoiceState?.IsMuted ?? false; /// public bool IsStreaming => VoiceState?.IsStreaming ?? false; /// public DateTimeOffset? JoinedAt => DateTimeUtils.FromTicks(_joinedAtTicks); /// /// Returns a collection of roles that the user possesses. /// public IReadOnlyCollection Roles => _roleIds.Select(id => Guild.GetRole(id)).Where(x => x != null).ToReadOnlyCollection(() => _roleIds.Length); /// /// Returns the voice channel the user is in, or null if none. /// public SocketVoiceChannel VoiceChannel => VoiceState?.VoiceChannel; /// public string VoiceSessionId => VoiceState?.VoiceSessionId ?? ""; /// /// Gets the voice connection status of the user if any. /// /// /// A representing the user's voice status; null if the user is not /// connected to a voice channel. /// public SocketVoiceState? VoiceState => Guild.GetVoiceState(Id); public AudioInStream AudioStream => Guild.GetAudioStream(Id); /// public DateTimeOffset? PremiumSince => DateTimeUtils.FromTicks(_premiumSinceTicks); /// /// Returns the position of the user within the role hierarchy. /// /// /// The returned value equal to the position of the highest role the user has, or /// if user is the server owner. /// public int Hierarchy { get { if (Guild.OwnerId == Id) return int.MaxValue; int maxPos = 0; for (int i = 0; i < _roleIds.Length; i++) { var role = Guild.GetRole(_roleIds[i]); if (role != null && role.Position > maxPos) maxPos = role.Position; } return maxPos; } } internal SocketGuildUser(SocketGuild guild, SocketGlobalUser globalUser) : base(guild.Discord, globalUser.Id) { Guild = guild; GlobalUser = globalUser; } internal static SocketGuildUser Create(SocketGuild guild, ClientState state, UserModel model) { var entity = new SocketGuildUser(guild, guild.Discord.GetOrCreateUser(state, model)); entity.Update(state, model); entity.UpdateRoles(new ulong[0]); return entity; } internal static SocketGuildUser Create(SocketGuild guild, ClientState state, MemberModel model) { var entity = new SocketGuildUser(guild, guild.Discord.GetOrCreateUser(state, model.User)); entity.Update(state, model); return entity; } internal static SocketGuildUser Create(SocketGuild guild, ClientState state, PresenceModel model) { var entity = new SocketGuildUser(guild, guild.Discord.GetOrCreateUser(state, model.User)); entity.Update(state, model, false); return entity; } internal void Update(ClientState state, MemberModel model) { base.Update(state, model.User); if (model.JoinedAt.IsSpecified) _joinedAtTicks = model.JoinedAt.Value.UtcTicks; if (model.Nick.IsSpecified) Nickname = model.Nick.Value; if (model.Roles.IsSpecified) UpdateRoles(model.Roles.Value); if (model.PremiumSince.IsSpecified) _premiumSinceTicks = model.PremiumSince.Value?.UtcTicks; } internal void Update(ClientState state, PresenceModel model, bool updatePresence) { if (updatePresence) { Presence = SocketPresence.Create(model); GlobalUser.Update(state, model); } if (model.Nick.IsSpecified) Nickname = model.Nick.Value; if (model.Roles.IsSpecified) UpdateRoles(model.Roles.Value); } private void UpdateRoles(ulong[] roleIds) { var roles = ImmutableArray.CreateBuilder(roleIds.Length + 1); roles.Add(Guild.Id); for (int i = 0; i < roleIds.Length; i++) roles.Add(roleIds[i]); _roleIds = roles.ToImmutable(); } /// public Task ModifyAsync(Action func, RequestOptions options = null) => UserHelper.ModifyAsync(this, Discord, func, options); /// public Task KickAsync(string reason = null, RequestOptions options = null) => UserHelper.KickAsync(this, Discord, reason, options); /// public Task AddRoleAsync(IRole role, RequestOptions options = null) => AddRolesAsync(new[] { role }, options); /// public Task AddRolesAsync(IEnumerable roles, RequestOptions options = null) => UserHelper.AddRolesAsync(this, Discord, roles, options); /// public Task RemoveRoleAsync(IRole role, RequestOptions options = null) => RemoveRolesAsync(new[] { role }, options); /// public Task RemoveRolesAsync(IEnumerable roles, RequestOptions options = null) => UserHelper.RemoveRolesAsync(this, Discord, roles, options); /// public ChannelPermissions GetPermissions(IGuildChannel channel) => new ChannelPermissions(Permissions.ResolveChannel(Guild, this, channel, GuildPermissions.RawValue)); private string DebuggerDisplay => $"{Username}#{Discriminator} ({Id}{(IsBot ? ", Bot" : "")}, Guild)"; internal new SocketGuildUser Clone() => MemberwiseClone() as SocketGuildUser; //IGuildUser /// IGuild IGuildUser.Guild => Guild; /// ulong IGuildUser.GuildId => Guild.Id; /// IReadOnlyCollection IGuildUser.RoleIds => _roleIds; //IVoiceState /// IVoiceChannel IVoiceState.VoiceChannel => VoiceChannel; } }