305 lines
13 KiB
C#
305 lines
13 KiB
C#
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 MemberModel = Discord.API.GuildMember;
|
|
using PresenceModel = Discord.API.Presence;
|
|
using UserModel = Discord.API.User;
|
|
|
|
namespace Discord.WebSocket
|
|
{
|
|
/// <summary>
|
|
/// Represents a WebSocket-based guild user.
|
|
/// </summary>
|
|
[DebuggerDisplay(@"{DebuggerDisplay,nq}")]
|
|
public class SocketGuildUser : SocketUser, IGuildUser
|
|
{
|
|
#region SocketGuildUser
|
|
private long? _premiumSinceTicks;
|
|
private long? _timedOutTicks;
|
|
private long? _joinedAtTicks;
|
|
private ImmutableArray<ulong> _roleIds;
|
|
|
|
internal override SocketGlobalUser GlobalUser { get; set; }
|
|
/// <summary>
|
|
/// Gets the guild the user is in.
|
|
/// </summary>
|
|
public SocketGuild Guild { get; }
|
|
|
|
/// <inheritdoc cref="IGuildUser.DisplayName"/>
|
|
public string DisplayName => Nickname ?? GlobalName ?? Username;
|
|
|
|
/// <inheritdoc />
|
|
public string Nickname { get; private set; }
|
|
/// <inheritdoc/>
|
|
public string DisplayAvatarId => GuildAvatarId ?? AvatarId;
|
|
/// <inheritdoc/>
|
|
public string GuildAvatarId { get; private set; }
|
|
/// <inheritdoc />
|
|
public override bool IsBot { get { return GlobalUser.IsBot; } internal set { GlobalUser.IsBot = value; } }
|
|
/// <inheritdoc />
|
|
public override string Username { get { return GlobalUser.Username; } internal set { GlobalUser.Username = value; } }
|
|
/// <inheritdoc />
|
|
public override ushort DiscriminatorValue { get { return GlobalUser.DiscriminatorValue; } internal set { GlobalUser.DiscriminatorValue = value; } }
|
|
/// <inheritdoc />
|
|
public override string AvatarId { get { return GlobalUser.AvatarId; } internal set { GlobalUser.AvatarId = value; } }
|
|
/// <inheritdoc />
|
|
public override string GlobalName { get { return GlobalUser.GlobalName; } internal set { GlobalUser.GlobalName = value; } }
|
|
|
|
/// <inheritdoc/>
|
|
public string GuildBannerHash { get; private set; }
|
|
|
|
/// <inheritdoc />
|
|
public GuildPermissions GuildPermissions => new GuildPermissions(Permissions.ResolveGuild(Guild, this));
|
|
internal override SocketPresence Presence { get; set; }
|
|
|
|
/// <inheritdoc />
|
|
public override bool IsWebhook => false;
|
|
/// <inheritdoc />
|
|
public bool IsSelfDeafened => VoiceState?.IsSelfDeafened ?? false;
|
|
/// <inheritdoc />
|
|
public bool IsSelfMuted => VoiceState?.IsSelfMuted ?? false;
|
|
/// <inheritdoc />
|
|
public bool IsSuppressed => VoiceState?.IsSuppressed ?? false;
|
|
/// <inheritdoc />
|
|
public bool IsDeafened => VoiceState?.IsDeafened ?? false;
|
|
/// <inheritdoc />
|
|
public bool IsMuted => VoiceState?.IsMuted ?? false;
|
|
/// <inheritdoc />
|
|
public bool IsStreaming => VoiceState?.IsStreaming ?? false;
|
|
/// <inheritdoc />
|
|
public bool IsVideoing => VoiceState?.IsVideoing ?? false;
|
|
/// <inheritdoc />
|
|
public DateTimeOffset? RequestToSpeakTimestamp => VoiceState?.RequestToSpeakTimestamp ?? null;
|
|
/// <inheritdoc />
|
|
public bool? IsPending { get; private set; }
|
|
|
|
/// <inheritdoc />
|
|
public GuildUserFlags Flags { get; private set; }
|
|
|
|
/// <inheritdoc />
|
|
public DateTimeOffset? JoinedAt => DateTimeUtils.FromTicks(_joinedAtTicks);
|
|
/// <summary>
|
|
/// Returns a collection of roles that the user possesses.
|
|
/// </summary>
|
|
public IReadOnlyCollection<SocketRole> Roles
|
|
=> _roleIds.Select(id => Guild.GetRole(id)).Where(x => x != null).ToReadOnlyCollection(() => _roleIds.Length);
|
|
/// <summary>
|
|
/// Returns the voice channel the user is in, or <see langword="null" /> if none.
|
|
/// </summary>
|
|
public SocketVoiceChannel VoiceChannel => VoiceState?.VoiceChannel;
|
|
/// <inheritdoc />
|
|
public string VoiceSessionId => VoiceState?.VoiceSessionId ?? "";
|
|
/// <summary>
|
|
/// Gets the voice connection status of the user if any.
|
|
/// </summary>
|
|
/// <returns>
|
|
/// A <see cref="SocketVoiceState" /> representing the user's voice status; <see langword="null" /> if the user is not
|
|
/// connected to a voice channel.
|
|
/// </returns>
|
|
public SocketVoiceState? VoiceState => Guild.GetVoiceState(Id);
|
|
public AudioInStream AudioStream => Guild.GetAudioStream(Id);
|
|
/// <inheritdoc />
|
|
public DateTimeOffset? PremiumSince => DateTimeUtils.FromTicks(_premiumSinceTicks);
|
|
/// <inheritdoc />
|
|
public DateTimeOffset? TimedOutUntil
|
|
{
|
|
get
|
|
{
|
|
if (!_timedOutTicks.HasValue || _timedOutTicks.Value < 0)
|
|
return null;
|
|
else
|
|
return DateTimeUtils.FromTicks(_timedOutTicks);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns the position of the user within the role hierarchy.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// The returned value equal to the position of the highest role the user has, or
|
|
/// <see cref="int.MaxValue"/> if user is the server owner.
|
|
/// </remarks>
|
|
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([]);
|
|
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);
|
|
if (!model.Roles.IsSpecified)
|
|
entity.UpdateRoles([]);
|
|
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);
|
|
if (!model.Roles.IsSpecified)
|
|
entity.UpdateRoles([]);
|
|
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.Avatar.IsSpecified)
|
|
GuildAvatarId = model.Avatar.Value;
|
|
if (model.Roles.IsSpecified)
|
|
UpdateRoles(model.Roles.Value);
|
|
if (model.PremiumSince.IsSpecified)
|
|
_premiumSinceTicks = model.PremiumSince.Value?.UtcTicks;
|
|
if (model.TimedOutUntil.IsSpecified)
|
|
_timedOutTicks = model.TimedOutUntil.Value?.UtcTicks;
|
|
if (model.Pending.IsSpecified)
|
|
IsPending = model.Pending.Value;
|
|
if (model.Banner.IsSpecified)
|
|
GuildBannerHash = model.Banner.Value;
|
|
|
|
Flags = model.Flags;
|
|
}
|
|
internal void Update(ClientState state, PresenceModel model, bool updatePresence)
|
|
{
|
|
if (updatePresence)
|
|
{
|
|
Update(model);
|
|
}
|
|
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 override void Update(PresenceModel model)
|
|
{
|
|
Presence ??= new SocketPresence();
|
|
|
|
Presence.Update(model);
|
|
GlobalUser.Update(model);
|
|
}
|
|
|
|
private void UpdateRoles(ulong[] roleIds)
|
|
{
|
|
var roles = ImmutableArray.CreateBuilder<ulong>(roleIds.Length + 1);
|
|
roles.Add(Guild.Id);
|
|
for (int i = 0; i < roleIds.Length; i++)
|
|
roles.Add(roleIds[i]);
|
|
_roleIds = roles.ToImmutable();
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public Task ModifyAsync(Action<GuildUserProperties> func, RequestOptions options = null)
|
|
=> UserHelper.ModifyAsync(this, Discord, func, options);
|
|
/// <inheritdoc />
|
|
public Task KickAsync(string reason = null, RequestOptions options = null)
|
|
=> UserHelper.KickAsync(this, Discord, reason, options);
|
|
/// <inheritdoc />
|
|
public Task AddRoleAsync(ulong roleId, RequestOptions options = null)
|
|
=> AddRolesAsync(new[] { roleId }, options);
|
|
/// <inheritdoc />
|
|
public Task AddRoleAsync(IRole role, RequestOptions options = null)
|
|
=> AddRoleAsync(role.Id, options);
|
|
/// <inheritdoc />
|
|
public Task AddRolesAsync(IEnumerable<ulong> roleIds, RequestOptions options = null)
|
|
=> UserHelper.AddRolesAsync(this, Discord, roleIds, options);
|
|
/// <inheritdoc />
|
|
public Task AddRolesAsync(IEnumerable<IRole> roles, RequestOptions options = null)
|
|
=> AddRolesAsync(roles.Select(x => x.Id), options);
|
|
/// <inheritdoc />
|
|
public Task RemoveRoleAsync(ulong roleId, RequestOptions options = null)
|
|
=> RemoveRolesAsync(new[] { roleId }, options);
|
|
/// <inheritdoc />
|
|
public Task RemoveRoleAsync(IRole role, RequestOptions options = null)
|
|
=> RemoveRoleAsync(role.Id, options);
|
|
/// <inheritdoc />
|
|
public Task RemoveRolesAsync(IEnumerable<ulong> roleIds, RequestOptions options = null)
|
|
=> UserHelper.RemoveRolesAsync(this, Discord, roleIds, options);
|
|
/// <inheritdoc />
|
|
public Task RemoveRolesAsync(IEnumerable<IRole> roles, RequestOptions options = null)
|
|
=> RemoveRolesAsync(roles.Select(x => x.Id));
|
|
/// <inheritdoc />
|
|
public Task SetTimeOutAsync(TimeSpan span, RequestOptions options = null)
|
|
=> UserHelper.SetTimeoutAsync(this, Discord, span, options);
|
|
/// <inheritdoc />
|
|
public Task RemoveTimeOutAsync(RequestOptions options = null)
|
|
=> UserHelper.RemoveTimeOutAsync(this, Discord, options);
|
|
/// <inheritdoc />
|
|
public ChannelPermissions GetPermissions(IGuildChannel channel)
|
|
=> new ChannelPermissions(Permissions.ResolveChannel(Guild, this, channel, GuildPermissions.RawValue));
|
|
|
|
/// <inheritdoc />
|
|
public string GetGuildAvatarUrl(ImageFormat format = ImageFormat.Auto, ushort size = 128)
|
|
=> CDN.GetGuildUserAvatarUrl(Id, Guild.Id, GuildAvatarId, size, format);
|
|
|
|
/// <inheritdoc />
|
|
public string GetGuildBannerUrl(ImageFormat format = ImageFormat.Auto, ushort size = 128)
|
|
=> CDN.GetGuildUserBannerUrl(Id, Guild.Id, GuildBannerHash, size, format);
|
|
|
|
/// <inheritdoc />
|
|
public override string GetDisplayAvatarUrl(ImageFormat format = ImageFormat.Auto, ushort size = 128)
|
|
=> GetGuildAvatarUrl(format, size) ?? base.GetDisplayAvatarUrl(format, size);
|
|
|
|
private string DebuggerDisplay => DiscriminatorValue != 0
|
|
? $"{Username}#{Discriminator} ({Id}{(IsBot ? ", Bot" : "")}, Guild)"
|
|
: $"{Username} ({Id}{(IsBot ? ", Bot" : "")}, Guild)";
|
|
|
|
internal new SocketGuildUser Clone()
|
|
{
|
|
var clone = MemberwiseClone() as SocketGuildUser;
|
|
clone.GlobalUser = GlobalUser.Clone();
|
|
return clone;
|
|
}
|
|
#endregion
|
|
|
|
#region IGuildUser
|
|
/// <inheritdoc />
|
|
IGuild IGuildUser.Guild => Guild;
|
|
/// <inheritdoc />
|
|
ulong IGuildUser.GuildId => Guild.Id;
|
|
/// <inheritdoc />
|
|
IReadOnlyCollection<ulong> IGuildUser.RoleIds => _roleIds;
|
|
|
|
//IVoiceState
|
|
/// <inheritdoc />
|
|
IVoiceChannel IVoiceState.VoiceChannel => VoiceChannel;
|
|
#endregion
|
|
}
|
|
}
|