@@ -97,6 +97,9 @@ | |||
<Compile Include="..\Discord.Net\Message.cs"> | |||
<Link>Message.cs</Link> | |||
</Compile> | |||
<Compile Include="..\Discord.Net\PackedPermissions.cs"> | |||
<Link>PackedPermissions.cs</Link> | |||
</Compile> | |||
<Compile Include="..\Discord.Net\Regions.cs"> | |||
<Link>Regions.cs</Link> | |||
</Compile> | |||
@@ -51,13 +51,52 @@ namespace Discord.API.Models | |||
[JsonProperty(PropertyName = "verified")] | |||
public bool IsVerified; | |||
} | |||
internal class PresenceUserInfo : UserReference | |||
internal class MemberInfo | |||
{ | |||
[JsonProperty(PropertyName = "user_id")] | |||
public string UserId; | |||
[JsonProperty(PropertyName = "user")] | |||
public UserReference User; | |||
[JsonProperty(PropertyName = "guild_id")] | |||
public string ServerId; | |||
} | |||
internal class PresenceMemberInfo : MemberInfo | |||
{ | |||
[JsonProperty(PropertyName = "game_id")] | |||
public string GameId; | |||
[JsonProperty(PropertyName = "status")] | |||
public string Status; | |||
} | |||
internal class VoiceMemberInfo : MemberInfo | |||
{ | |||
[JsonProperty(PropertyName = "channel_id")] | |||
public string ChannelId; | |||
[JsonProperty(PropertyName = "suppress")] | |||
public bool IsSuppressed; | |||
[JsonProperty(PropertyName = "session_id")] | |||
public string SessionId; | |||
[JsonProperty(PropertyName = "self_mute")] | |||
public bool IsSelfMuted; | |||
[JsonProperty(PropertyName = "self_deaf")] | |||
public bool IsSelfDeafened; | |||
[JsonProperty(PropertyName = "mute")] | |||
public bool IsMuted; | |||
[JsonProperty(PropertyName = "deaf")] | |||
public bool IsDeafened; | |||
[JsonProperty(PropertyName = "token")] | |||
public string Token; | |||
} | |||
internal class RoleMemberInfo : MemberInfo | |||
{ | |||
[JsonProperty(PropertyName = "mute")] | |||
public bool IsMuted; | |||
[JsonProperty(PropertyName = "deaf")] | |||
public bool IsDeafened; | |||
[JsonProperty(PropertyName = "joined_at")] | |||
public DateTime? JoinedAt; | |||
[JsonProperty(PropertyName = "roles")] | |||
public string[] Roles; | |||
} | |||
//Channels | |||
internal class ChannelReference | |||
@@ -73,12 +112,26 @@ namespace Discord.API.Models | |||
} | |||
internal class ChannelInfo : ChannelReference | |||
{ | |||
public sealed class PermissionOverwrite | |||
{ | |||
[JsonProperty(PropertyName = "type")] | |||
public string Type; | |||
[JsonProperty(PropertyName = "id")] | |||
public string Id; | |||
[JsonProperty(PropertyName = "deny")] | |||
public uint Deny; | |||
[JsonProperty(PropertyName = "allow")] | |||
public uint Allow; | |||
} | |||
[JsonProperty(PropertyName = "last_message_id")] | |||
public string LastMessageId; | |||
[JsonProperty(PropertyName = "is_private")] | |||
public bool IsPrivate; | |||
[JsonProperty(PropertyName = "position")] | |||
public int Position; | |||
[JsonProperty(PropertyName = "permission_overwrites")] | |||
public object[] PermissionOverwrites; | |||
public PermissionOverwrite[] PermissionOverwrites; | |||
[JsonProperty(PropertyName = "recipient")] | |||
public UserReference Recipient; | |||
} | |||
@@ -114,28 +167,14 @@ namespace Discord.API.Models | |||
} | |||
internal class ExtendedServerInfo : ServerInfo | |||
{ | |||
public class Membership | |||
{ | |||
[JsonProperty(PropertyName = "roles")] | |||
public string[] Roles; | |||
[JsonProperty(PropertyName = "mute")] | |||
public bool IsMuted; | |||
[JsonProperty(PropertyName = "deaf")] | |||
public bool IsDeaf; | |||
[JsonProperty(PropertyName = "joined_at")] | |||
public DateTime JoinedAt; | |||
[JsonProperty(PropertyName = "user")] | |||
public UserReference User; | |||
} | |||
[JsonProperty(PropertyName = "channels")] | |||
public ChannelInfo[] Channels; | |||
[JsonProperty(PropertyName = "members")] | |||
public Membership[] Members; | |||
[JsonProperty(PropertyName = "presence")] | |||
public object[] Presence; | |||
public RoleMemberInfo[] Members; | |||
[JsonProperty(PropertyName = "presences")] | |||
public PresenceMemberInfo[] Presences; | |||
[JsonProperty(PropertyName = "voice_states")] | |||
public object[] VoiceStates; | |||
public VoiceMemberInfo[] VoiceStates; | |||
} | |||
//Messages | |||
@@ -150,7 +189,7 @@ namespace Discord.API.Models | |||
} | |||
internal class Message : MessageReference | |||
{ | |||
public class Attachment | |||
public sealed class Attachment | |||
{ | |||
[JsonProperty(PropertyName = "id")] | |||
public string Id; | |||
@@ -167,6 +206,40 @@ namespace Discord.API.Models | |||
[JsonProperty(PropertyName = "height")] | |||
public int Height; | |||
} | |||
public sealed class Embed | |||
{ | |||
public sealed class ProviderInfo | |||
{ | |||
[JsonProperty(PropertyName = "url")] | |||
public string Url; | |||
[JsonProperty(PropertyName = "name")] | |||
public string Name; | |||
} | |||
public sealed class ThumbnailInfo | |||
{ | |||
[JsonProperty(PropertyName = "url")] | |||
public string Url; | |||
[JsonProperty(PropertyName = "proxy_url")] | |||
public string ProxyUrl; | |||
[JsonProperty(PropertyName = "width")] | |||
public int Width; | |||
[JsonProperty(PropertyName = "height")] | |||
public int Height; | |||
} | |||
[JsonProperty(PropertyName = "url")] | |||
public string Url; | |||
[JsonProperty(PropertyName = "type")] | |||
public string Type; | |||
[JsonProperty(PropertyName = "title")] | |||
public string Title; | |||
[JsonProperty(PropertyName = "description")] | |||
public string Description; | |||
[JsonProperty(PropertyName = "provider")] | |||
public ProviderInfo Provider; | |||
[JsonProperty(PropertyName = "thumbnail")] | |||
public ThumbnailInfo Thumbnail; | |||
} | |||
[JsonProperty(PropertyName = "tts")] | |||
public bool IsTextToSpeech; | |||
@@ -179,7 +252,7 @@ namespace Discord.API.Models | |||
[JsonProperty(PropertyName = "mentions")] | |||
public UserReference[] Mentions; | |||
[JsonProperty(PropertyName = "embeds")] | |||
public object[] Embeds; //TODO: Parse this | |||
public Embed[] Embeds; //TODO: Parse this | |||
[JsonProperty(PropertyName = "attachments")] | |||
public Attachment[] Attachments; | |||
[JsonProperty(PropertyName = "content")] | |||
@@ -22,6 +22,8 @@ namespace Discord.API.Models | |||
{ | |||
[JsonProperty(PropertyName = "token")] | |||
public string Token; | |||
[JsonProperty(PropertyName = "v")] | |||
public int Version = 2; | |||
[JsonProperty(PropertyName = "properties")] | |||
public Dictionary<string, string> Properties = new Dictionary<string, string>(); | |||
} | |||
@@ -11,12 +11,24 @@ namespace Discord.API.Models | |||
{ | |||
public sealed class Ready | |||
{ | |||
public sealed class ReadStateInfo | |||
{ | |||
[JsonProperty(PropertyName = "id")] | |||
public string ChannelId; | |||
[JsonProperty(PropertyName = "mention_count")] | |||
public int MentionCount; | |||
[JsonProperty(PropertyName = "last_message_id")] | |||
public string LastMessageId; | |||
} | |||
[JsonProperty(PropertyName = "v")] | |||
public int Version; | |||
[JsonProperty(PropertyName = "user")] | |||
public SelfUserInfo User; | |||
[JsonProperty(PropertyName = "session_id")] | |||
public string SessionId; | |||
[JsonProperty(PropertyName = "read_state")] | |||
public object[] ReadState; | |||
public ReadStateInfo[] ReadState; | |||
[JsonProperty(PropertyName = "guilds")] | |||
public ExtendedServerInfo[] Guilds; | |||
[JsonProperty(PropertyName = "private_channels")] | |||
@@ -36,32 +48,15 @@ namespace Discord.API.Models | |||
public sealed class ChannelUpdate : ChannelInfo { } | |||
//Memberships | |||
public abstract class GuildMemberEvent | |||
{ | |||
[JsonProperty(PropertyName = "user")] | |||
public UserReference User; | |||
[JsonProperty(PropertyName = "guild_id")] | |||
public string GuildId; | |||
} | |||
public sealed class GuildMemberAdd : GuildMemberEvent | |||
{ | |||
[JsonProperty(PropertyName = "joined_at")] | |||
public DateTime JoinedAt; | |||
[JsonProperty(PropertyName = "roles")] | |||
public string[] Roles; | |||
} | |||
public sealed class GuildMemberUpdate : GuildMemberEvent | |||
{ | |||
[JsonProperty(PropertyName = "roles")] | |||
public string[] Roles; | |||
} | |||
public sealed class GuildMemberRemove : GuildMemberEvent { } | |||
public sealed class GuildMemberAdd : RoleMemberInfo { } | |||
public sealed class GuildMemberUpdate : RoleMemberInfo { } | |||
public sealed class GuildMemberRemove : MemberInfo { } | |||
//Roles | |||
public abstract class GuildRoleEvent | |||
{ | |||
[JsonProperty(PropertyName = "guild_id")] | |||
public string GuildId; | |||
public string ServerId; | |||
} | |||
public sealed class GuildRoleCreateUpdate : GuildRoleEvent | |||
{ | |||
@@ -78,7 +73,7 @@ namespace Discord.API.Models | |||
public abstract class GuildBanEvent | |||
{ | |||
[JsonProperty(PropertyName = "guild_id")] | |||
public string GuildId; | |||
public string ServerId; | |||
} | |||
public sealed class GuildBanAddRemove : GuildBanEvent | |||
{ | |||
@@ -93,28 +88,8 @@ namespace Discord.API.Models | |||
//User | |||
public sealed class UserUpdate : SelfUserInfo { } | |||
public sealed class PresenceUpdate : PresenceUserInfo { } | |||
public sealed class VoiceStateUpdate | |||
{ | |||
[JsonProperty(PropertyName = "user_id")] | |||
public string UserId; | |||
[JsonProperty(PropertyName = "guild_id")] | |||
public string GuildId; | |||
[JsonProperty(PropertyName = "channel_id")] | |||
public string ChannelId; | |||
[JsonProperty(PropertyName = "suppress")] | |||
public bool IsSuppressed; | |||
[JsonProperty(PropertyName = "session_id")] | |||
public string SessionId; | |||
[JsonProperty(PropertyName = "self_mute")] | |||
public bool IsSelfMuted; | |||
[JsonProperty(PropertyName = "self_deaf")] | |||
public bool IsSelfDeafened; | |||
[JsonProperty(PropertyName = "mute")] | |||
public bool IsMuted; | |||
[JsonProperty(PropertyName = "deaf")] | |||
public bool IsDeafened; | |||
} | |||
public sealed class PresenceUpdate : PresenceMemberInfo { } | |||
public sealed class VoiceStateUpdate : VoiceMemberInfo { } | |||
//Chat | |||
public sealed class MessageCreate : Message { } | |||
@@ -4,8 +4,16 @@ using System.Linq; | |||
namespace Discord | |||
{ | |||
public sealed class Channel | |||
public sealed class Channel | |||
{ | |||
public sealed class PermissionOverwrite | |||
{ | |||
public string Type { get; internal set; } | |||
public string Id { get; internal set; } | |||
public PackedPermissions Deny { get; internal set; } | |||
public PackedPermissions Allow { get; internal set; } | |||
} | |||
private readonly DiscordClient _client; | |||
/// <summary> Returns the unique identifier for this channel. </summary> | |||
@@ -15,8 +23,10 @@ namespace Discord | |||
/// <summary> Returns the name of this channel. </summary> | |||
public string Name { get { return !IsPrivate ? $"#{_name}" : $"@{Recipient.Name}"; } internal set { _name = value; } } | |||
/// <summary> Returns the position of this channel in the channel list for this server. </summary> | |||
public int Position { get; internal set; } | |||
/// <summary> Returns false is this is a public chat and true if this is a private chat with another user (see Recipient). </summary> | |||
public bool IsPrivate { get; } | |||
public bool IsPrivate { get; } | |||
/// <summary> Returns the type of this channel (see ChannelTypes). </summary> | |||
public string Type { get; internal set; } | |||
@@ -37,9 +47,8 @@ namespace Discord | |||
[JsonIgnore] | |||
public IEnumerable<Message> Messages => _client.Messages.Where(x => x.ChannelId == Id); | |||
//TODO: Not Implemented | |||
/// <summary> Not implemented, stored for reference. </summary> | |||
public object[] PermissionOverwrites { get; internal set; } | |||
/// <summary> Returns a collection of all custom permissions used for this channel. </summary> | |||
public PermissionOverwrite[] PermissionOverwrites { get; internal set; } | |||
internal Channel(string id, string serverId, DiscordClient client) | |||
{ | |||
@@ -187,19 +187,19 @@ namespace Discord | |||
} | |||
public event EventHandler<MemberEventArgs> MemberAdded; | |||
private void RaiseMemberAdded(Membership membership, Server server) | |||
private void RaiseMemberAdded(Membership membership) | |||
{ | |||
if (MemberAdded != null) | |||
MemberAdded(this, new MemberEventArgs(membership)); | |||
} | |||
public event EventHandler<MemberEventArgs> MemberRemoved; | |||
private void RaiseMemberRemoved(Membership membership, Server server) | |||
private void RaiseMemberRemoved(Membership membership) | |||
{ | |||
if (MemberRemoved != null) | |||
MemberRemoved(this, new MemberEventArgs(membership)); | |||
} | |||
public event EventHandler<MemberEventArgs> MemberUpdated; | |||
private void RaiseMemberUpdated(Membership membership, Server server) | |||
private void RaiseMemberUpdated(Membership membership) | |||
{ | |||
if (MemberUpdated != null) | |||
MemberUpdated(this, new MemberEventArgs(membership)); | |||
@@ -217,11 +217,11 @@ namespace Discord | |||
} | |||
} | |||
public event EventHandler<UserEventArgs> PresenceUpdated; | |||
private void RaisePresenceUpdated(User user) | |||
public event EventHandler<MemberEventArgs> PresenceUpdated; | |||
private void RaisePresenceUpdated(Membership member) | |||
{ | |||
if (PresenceUpdated != null) | |||
PresenceUpdated(this, new UserEventArgs(user)); | |||
PresenceUpdated(this, new MemberEventArgs(member)); | |||
} | |||
public event EventHandler<MemberEventArgs> VoiceStateUpdated; | |||
private void RaiseVoiceStateUpdated(Membership member) | |||
@@ -1,6 +1,7 @@ | |||
using Discord.API; | |||
using Discord.API.Models; | |||
using Discord.Helpers; | |||
using Newtonsoft.Json; | |||
using System; | |||
using System.Collections.Generic; | |||
using System.IO; | |||
@@ -19,10 +20,13 @@ namespace Discord | |||
private ManualResetEventSlim _isStopping; | |||
private readonly Regex _userRegex, _channelRegex; | |||
private readonly MatchEvaluator _userRegexEvaluator, _channelRegexEvaluator; | |||
private readonly JsonSerializer _serializer; | |||
/// <summary> Returns the User object for the current logged in user. </summary> | |||
public User User { get; private set; } | |||
/// <summary> Returns the id of the current logged in user. </summary> | |||
public string UserId { get; private set; } | |||
public string SessionId { get; private set; } | |||
/// <summary> Returns a collection of all users the client can see across all servers. </summary> | |||
/// <remarks> This collection does not guarantee any ordering. </remarks> | |||
@@ -64,6 +68,12 @@ namespace Discord | |||
{ | |||
_isStopping = new ManualResetEventSlim(false); | |||
_serializer = new JsonSerializer(); | |||
#if TEST_RESPONSES | |||
_serializer.CheckAdditionalContent = true; | |||
_serializer.MissingMemberHandling = MissingMemberHandling.Error; | |||
#endif | |||
_userRegex = new Regex(@"<@\d+?>", RegexOptions.Compiled); | |||
_channelRegex = new Regex(@"<#\d+?>", RegexOptions.Compiled); | |||
_userRegexEvaluator = new MatchEvaluator(e => | |||
@@ -90,11 +100,7 @@ namespace Discord | |||
(server, model) => | |||
{ | |||
server.Name = model.Name; | |||
if (!server.Channels.Any()) //A default channel always exists with the same id as the server. | |||
{ | |||
var defaultChannel = new ChannelReference() { Id = server.DefaultChannelId, GuildId = server.Id }; | |||
_channels.Update(defaultChannel.Id, defaultChannel.GuildId, defaultChannel); | |||
} | |||
_channels.Update(server.DefaultChannelId, server.Id, null); | |||
if (model is ExtendedServerInfo) | |||
{ | |||
var extendedModel = model as ExtendedServerInfo; | |||
@@ -102,9 +108,7 @@ namespace Discord | |||
server.AFKTimeout = extendedModel.AFKTimeout; | |||
server.JoinedAt = extendedModel.JoinedAt ?? DateTime.MinValue; | |||
server.OwnerId = extendedModel.OwnerId; | |||
server.Presence = extendedModel.Presence; | |||
server.Region = extendedModel.Region; | |||
server.VoiceStates = extendedModel.VoiceStates; | |||
foreach (var role in extendedModel.Roles) | |||
_roles.Update(role.Id, model.Id, role); | |||
@@ -113,11 +117,13 @@ namespace Discord | |||
foreach (var membership in extendedModel.Members) | |||
{ | |||
_users.Update(membership.User.Id, membership.User); | |||
var newMember = new Membership(server.Id, membership.User.Id, membership.JoinedAt, this); | |||
newMember.Update(membership); | |||
server.AddMember(newMember); | |||
server.UpdateMember(membership); | |||
} | |||
} | |||
foreach (var membership in extendedModel.VoiceStates) | |||
server.UpdateMember(membership); | |||
foreach (var membership in extendedModel.Presences) | |||
server.UpdateMember(membership); | |||
} | |||
}, | |||
server => { } | |||
); | |||
@@ -131,13 +137,27 @@ namespace Discord | |||
if (model is ChannelInfo) | |||
{ | |||
var extendedModel = model as ChannelInfo; | |||
channel.PermissionOverwrites = extendedModel.PermissionOverwrites; | |||
channel.Position = extendedModel.Position; | |||
if (extendedModel.IsPrivate) | |||
{ | |||
var user = _users.Update(extendedModel.Recipient.Id, extendedModel.Recipient); | |||
channel.RecipientId = user.Id; | |||
channel.RecipientId = user.Id; | |||
user.PrivateChannelId = channel.Id; | |||
} | |||
if (extendedModel.PermissionOverwrites != null) | |||
{ | |||
channel.PermissionOverwrites = extendedModel.PermissionOverwrites.Select(x => new Channel.PermissionOverwrite | |||
{ | |||
Type = x.Type, | |||
Id = x.Id, | |||
Deny = new PackedPermissions(x.Deny), | |||
Allow = new PackedPermissions(x.Allow) | |||
}).ToArray(); | |||
} | |||
else | |||
channel.PermissionOverwrites = null; | |||
} | |||
}, | |||
channel => | |||
@@ -170,8 +190,41 @@ namespace Discord | |||
}).ToArray(); | |||
} | |||
else | |||
extendedModel.Attachments = null; | |||
message.Embeds = extendedModel.Embeds; | |||
message.Attachments = new Message.Attachment[0]; | |||
if (extendedModel.Embeds != null) | |||
{ | |||
message.Embeds = extendedModel.Embeds.Select(x => | |||
{ | |||
var embed = new Message.Embed | |||
{ | |||
Url = x.Url, | |||
Type = x.Type, | |||
Description = x.Description, | |||
Title = x.Title | |||
}; | |||
if (x.Provider != null) | |||
{ | |||
embed.Provider = new Message.EmbedProvider | |||
{ | |||
Url = x.Provider.Url, | |||
Name = x.Provider.Name | |||
}; | |||
} | |||
if (x.Thumbnail != null) | |||
{ | |||
embed.Thumbnail = new Message.File | |||
{ | |||
Url = x.Thumbnail.Url, | |||
ProxyUrl = x.Thumbnail.ProxyUrl, | |||
Width = x.Thumbnail.Width, | |||
Height = x.Thumbnail.Height | |||
}; | |||
} | |||
return embed; | |||
}).ToArray(); | |||
} | |||
else | |||
message.Embeds = new Message.Embed[0]; | |||
message.IsMentioningEveryone = extendedModel.IsMentioningEveryone; | |||
message.IsTTS = extendedModel.IsTextToSpeech; | |||
message.MentionIds = extendedModel.Mentions?.Select(x => x.Id)?.ToArray() ?? new string[0]; | |||
@@ -206,12 +259,6 @@ namespace Discord | |||
var extendedModel = model as SelfUserInfo; | |||
user.Email = extendedModel.Email; | |||
user.IsVerified = extendedModel.IsVerified; | |||
} | |||
if (model is PresenceUserInfo) | |||
{ | |||
var extendedModel = model as PresenceUserInfo; | |||
user.GameId = extendedModel.GameId; | |||
user.Status = extendedModel.Status; | |||
} | |||
}, | |||
user => { } | |||
@@ -246,101 +293,104 @@ namespace Discord | |||
//Global | |||
case "READY": //Resync | |||
{ | |||
var data = e.Event.ToObject<WebSocketEvents.Ready>(); | |||
var data = e.Event.ToObject<WebSocketEvents.Ready>(_serializer); | |||
_servers.Clear(); | |||
_channels.Clear(); | |||
_users.Clear(); | |||
UserId = data.User.Id; | |||
SessionId = data.SessionId; | |||
User = _users.Update(data.User.Id, data.User); | |||
foreach (var server in data.Guilds) | |||
_servers.Update(server.Id, server); | |||
foreach (var channel in data.PrivateChannels) | |||
_channels.Update(channel.Id, null, channel); | |||
} | |||
} | |||
break; | |||
//Servers | |||
case "GUILD_CREATE": | |||
{ | |||
var data = e.Event.ToObject<WebSocketEvents.GuildCreate>(); | |||
var data = e.Event.ToObject<WebSocketEvents.GuildCreate>(_serializer); | |||
var server = _servers.Update(data.Id, data); | |||
RaiseServerCreated(server); | |||
try { RaiseServerCreated(server); } catch { } | |||
} | |||
break; | |||
case "GUILD_UPDATE": | |||
{ | |||
var data = e.Event.ToObject<WebSocketEvents.GuildUpdate>(); | |||
var data = e.Event.ToObject<WebSocketEvents.GuildUpdate>(_serializer); | |||
var server = _servers.Update(data.Id, data); | |||
RaiseServerUpdated(server); | |||
try { RaiseServerUpdated(server); } catch { } | |||
} | |||
break; | |||
case "GUILD_DELETE": | |||
{ | |||
var data = e.Event.ToObject<WebSocketEvents.GuildDelete>(); | |||
var data = e.Event.ToObject<WebSocketEvents.GuildDelete>(_serializer); | |||
var server = _servers.Remove(data.Id); | |||
if (server != null) | |||
RaiseServerDestroyed(server); | |||
try { RaiseServerDestroyed(server); } catch { } | |||
} | |||
break; | |||
//Channels | |||
case "CHANNEL_CREATE": | |||
{ | |||
var data = e.Event.ToObject<WebSocketEvents.ChannelCreate>(); | |||
var data = e.Event.ToObject<WebSocketEvents.ChannelCreate>(_serializer); | |||
var channel = _channels.Update(data.Id, data.GuildId, data); | |||
RaiseChannelCreated(channel); | |||
try { RaiseChannelCreated(channel); } catch { } | |||
} | |||
break; | |||
case "CHANNEL_UPDATE": | |||
{ | |||
var data = e.Event.ToObject<WebSocketEvents.ChannelUpdate>(); | |||
var data = e.Event.ToObject<WebSocketEvents.ChannelUpdate>(_serializer); | |||
var channel = _channels.Update(data.Id, data.GuildId, data); | |||
RaiseChannelUpdated(channel); | |||
try { RaiseChannelUpdated(channel); } catch { } | |||
} | |||
break; | |||
case "CHANNEL_DELETE": | |||
{ | |||
var data = e.Event.ToObject<WebSocketEvents.ChannelDelete>(); | |||
var data = e.Event.ToObject<WebSocketEvents.ChannelDelete>(_serializer); | |||
var channel = _channels.Remove(data.Id); | |||
if (channel != null) | |||
RaiseChannelDestroyed(channel); | |||
try { RaiseChannelDestroyed(channel); } catch { } | |||
} | |||
break; | |||
//Members | |||
case "GUILD_MEMBER_ADD": | |||
{ | |||
var data = e.Event.ToObject<WebSocketEvents.GuildMemberAdd>(); | |||
var data = e.Event.ToObject<WebSocketEvents.GuildMemberAdd>(_serializer); | |||
var user = _users.Update(data.User.Id, data.User); | |||
var server = _servers[data.GuildId]; | |||
var membership = new Membership(server.Id, data.User.Id, data.JoinedAt, this) { RoleIds = data.Roles }; | |||
server.AddMember(membership); | |||
RaiseMemberAdded(membership, server); | |||
var server = _servers[data.ServerId]; | |||
if (server != null) | |||
{ | |||
var member = server.UpdateMember(data); | |||
try { RaiseMemberAdded(member); } catch { } | |||
} | |||
} | |||
break; | |||
case "GUILD_MEMBER_UPDATE": | |||
{ | |||
var data = e.Event.ToObject<WebSocketEvents.GuildMemberUpdate>(); | |||
var data = e.Event.ToObject<WebSocketEvents.GuildMemberUpdate>(_serializer); | |||
var user = _users.Update(data.User.Id, data.User); | |||
var server = _servers[data.GuildId]; | |||
var membership = server.GetMember(data.User.Id); | |||
if (membership != null) | |||
membership.RoleIds = data.Roles; | |||
RaiseMemberUpdated(membership, server); | |||
var server = _servers[data.ServerId]; | |||
if (server != null) | |||
{ | |||
var member = server.UpdateMember(data); | |||
try { RaiseMemberUpdated(member); } catch { } | |||
} | |||
} | |||
break; | |||
case "GUILD_MEMBER_REMOVE": | |||
{ | |||
var data = e.Event.ToObject<WebSocketEvents.GuildMemberRemove>(); | |||
var user = _users.Update(data.User.Id, data.User); | |||
var server = _servers[data.GuildId]; | |||
var data = e.Event.ToObject<WebSocketEvents.GuildMemberRemove>(_serializer); | |||
var server = _servers[data.ServerId]; | |||
if (server != null) | |||
{ | |||
var membership = server.RemoveMember(user.Id); | |||
if (membership != null) | |||
RaiseMemberRemoved(membership, server); | |||
var member = server.RemoveMember(data.UserId); | |||
if (member != null) | |||
try { RaiseMemberRemoved(member); } catch { } | |||
} | |||
} | |||
break; | |||
@@ -348,121 +398,138 @@ namespace Discord | |||
//Roles | |||
case "GUILD_ROLE_CREATE": | |||
{ | |||
var data = e.Event.ToObject<WebSocketEvents.GuildRoleCreateUpdate>(); | |||
var role = _roles.Update(data.Role.Id, data.GuildId, data.Role); | |||
RaiseRoleCreated(role); | |||
var data = e.Event.ToObject<WebSocketEvents.GuildRoleCreateUpdate>(_serializer); | |||
var role = _roles.Update(data.Role.Id, data.ServerId, data.Role); | |||
try { RaiseRoleCreated(role); } catch { } | |||
} | |||
break; | |||
case "GUILD_ROLE_UPDATE": | |||
{ | |||
var data = e.Event.ToObject<WebSocketEvents.GuildRoleCreateUpdate>(); | |||
var role = _roles.Update(data.Role.Id, data.GuildId, data.Role); | |||
RaiseRoleUpdated(role); | |||
var data = e.Event.ToObject<WebSocketEvents.GuildRoleCreateUpdate>(_serializer); | |||
var role = _roles.Update(data.Role.Id, data.ServerId, data.Role); | |||
try { RaiseRoleUpdated(role); } catch { } | |||
} | |||
break; | |||
case "GUILD_ROLE_DELETE": | |||
{ | |||
var data = e.Event.ToObject<WebSocketEvents.GuildRoleDelete>(); | |||
var data = e.Event.ToObject<WebSocketEvents.GuildRoleDelete>(_serializer); | |||
var role = _roles.Remove(data.RoleId); | |||
if (role != null) | |||
RaiseRoleDeleted(role); | |||
try { RaiseRoleDeleted(role); } catch { } | |||
} | |||
break; | |||
//Bans | |||
case "GUILD_BAN_ADD": | |||
{ | |||
var data = e.Event.ToObject<WebSocketEvents.GuildBanAddRemove>(); | |||
var data = e.Event.ToObject<WebSocketEvents.GuildBanAddRemove>(_serializer); | |||
var user = _users.Update(data.User.Id, data.User); | |||
var server = _servers[data.GuildId]; | |||
RaiseBanAdded(user, server); | |||
var server = _servers[data.ServerId]; | |||
try { RaiseBanAdded(user, server); } catch { } | |||
} | |||
break; | |||
case "GUILD_BAN_REMOVE": | |||
{ | |||
var data = e.Event.ToObject<WebSocketEvents.GuildBanAddRemove>(); | |||
var data = e.Event.ToObject<WebSocketEvents.GuildBanAddRemove>(_serializer); | |||
var user = _users.Update(data.User.Id, data.User); | |||
var server = _servers[data.GuildId]; | |||
var server = _servers[data.ServerId]; | |||
if (server != null && server.RemoveBan(user.Id)) | |||
RaiseBanRemoved(user, server); | |||
{ | |||
try { RaiseBanRemoved(user, server); } catch { } | |||
} | |||
} | |||
break; | |||
//Messages | |||
case "MESSAGE_CREATE": | |||
{ | |||
var data = e.Event.ToObject<WebSocketEvents.MessageCreate>(); | |||
var data = e.Event.ToObject<WebSocketEvents.MessageCreate>(_serializer); | |||
var msg = _messages.Update(data.Id, data.ChannelId, data); | |||
msg.User.UpdateActivity(data.Timestamp); | |||
RaiseMessageCreated(msg); | |||
try { RaiseMessageCreated(msg); } catch { } | |||
} | |||
break; | |||
case "MESSAGE_UPDATE": | |||
{ | |||
var data = e.Event.ToObject<WebSocketEvents.MessageUpdate>(); | |||
var data = e.Event.ToObject<WebSocketEvents.MessageUpdate>(_serializer); | |||
var msg = _messages.Update(data.Id, data.ChannelId, data); | |||
RaiseMessageUpdated(msg); | |||
try { RaiseMessageUpdated(msg); } catch { } | |||
} | |||
break; | |||
case "MESSAGE_DELETE": | |||
{ | |||
var data = e.Event.ToObject<WebSocketEvents.MessageDelete>(); | |||
var data = e.Event.ToObject<WebSocketEvents.MessageDelete>(_serializer); | |||
var msg = GetMessage(data.MessageId); | |||
if (msg != null) | |||
{ | |||
_messages.Remove(msg.Id); | |||
try { RaiseMessageDeleted(msg); } catch { } | |||
} | |||
} | |||
break; | |||
case "MESSAGE_ACK": | |||
{ | |||
var data = e.Event.ToObject<WebSocketEvents.MessageAck>(); | |||
var data = e.Event.ToObject<WebSocketEvents.MessageAck>(_serializer); | |||
var msg = GetMessage(data.MessageId); | |||
RaiseMessageAcknowledged(msg); | |||
if (msg != null) | |||
try { RaiseMessageAcknowledged(msg); } catch { } | |||
} | |||
break; | |||
//Statuses | |||
case "PRESENCE_UPDATE": | |||
{ | |||
var data = e.Event.ToObject<WebSocketEvents.PresenceUpdate>(); | |||
var user = _users.Update(data.Id, data); | |||
RaisePresenceUpdated(user); | |||
var data = e.Event.ToObject<WebSocketEvents.PresenceUpdate>(_serializer); | |||
var user = _users.Update(data.User.Id, data.User); | |||
var server = _servers[data.ServerId]; | |||
if (server != null) | |||
{ | |||
var member = server.UpdateMember(data); | |||
try { RaisePresenceUpdated(member); } catch { } | |||
} | |||
} | |||
break; | |||
case "VOICE_STATE_UPDATE": | |||
{ | |||
var data = e.Event.ToObject<WebSocketEvents.VoiceStateUpdate>(); | |||
var member = GetMember(data.GuildId, data.UserId); | |||
if (member != null) | |||
var data = e.Event.ToObject<WebSocketEvents.VoiceStateUpdate>(_serializer); | |||
var server = _servers[data.ServerId]; | |||
if (server != null) | |||
{ | |||
member.Update(data); | |||
RaiseVoiceStateUpdated(member); | |||
var member = server.UpdateMember(data); | |||
if (member != null) | |||
try { RaiseVoiceStateUpdated(member); } catch { } | |||
} | |||
} | |||
break; | |||
case "TYPING_START": | |||
{ | |||
var data = e.Event.ToObject<WebSocketEvents.TypingStart>(); | |||
var data = e.Event.ToObject<WebSocketEvents.TypingStart>(_serializer); | |||
var channel = _channels[data.ChannelId]; | |||
var user = _users[data.UserId]; | |||
RaiseUserTyping(user, channel); | |||
if (user != null) | |||
{ | |||
user.UpdateActivity(DateTime.UtcNow); | |||
if (channel != null) | |||
try { RaiseUserTyping(user, channel); } catch { } | |||
} | |||
} | |||
break; | |||
//Voice | |||
case "VOICE_SERVER_UPDATE": | |||
{ | |||
var data = e.Event.ToObject<WebSocketEvents.VoiceServerUpdate>(); | |||
var data = e.Event.ToObject<WebSocketEvents.VoiceServerUpdate>(_serializer); | |||
var server = _servers[data.ServerId]; | |||
RaiseVoiceServerUpdated(server, data.Endpoint); | |||
try { RaiseVoiceServerUpdated(server, data.Endpoint); } catch { } | |||
} | |||
break; | |||
//Settings | |||
case "USER_UPDATE": | |||
{ | |||
var data = e.Event.ToObject<WebSocketEvents.UserUpdate>(); | |||
var data = e.Event.ToObject<WebSocketEvents.UserUpdate>(_serializer); | |||
var user = _users.Update(data.Id, data); | |||
RaiseUserUpdated(user); | |||
try { RaiseUserUpdated(user); } catch { } | |||
} | |||
break; | |||
case "USER_SETTINGS_UPDATE": | |||
@@ -98,7 +98,8 @@ namespace Discord | |||
{ | |||
throw new InvalidOperationException("Bad Token"); | |||
} | |||
_connectWaitOnLogin2.Wait(cancelToken); //Waiting on READY handler | |||
try { _connectWaitOnLogin2.Wait(cancelToken); } //Waiting on READY handler | |||
catch (OperationCanceledException) { return; } | |||
_isConnected = true; | |||
RaiseConnected(); | |||
@@ -150,11 +151,7 @@ namespace Discord | |||
QueueMessage(new WebSocketCommands.KeepAlive()); | |||
_connectWaitOnLogin.Set(); //Pre-Event | |||
} | |||
try | |||
{ | |||
RaiseGotEvent(msg.Type, msg.Payload as JToken); | |||
} | |||
catch { } //Don't allow user exceptions to affect our state | |||
RaiseGotEvent(msg.Type, msg.Payload as JToken); | |||
if (msg.Type == "READY") | |||
_connectWaitOnLogin2.Set(); //Post-Event | |||
break; | |||
@@ -14,7 +14,7 @@ namespace Discord.Helpers | |||
private readonly Action<TValue, TModel> _onUpdate; | |||
private readonly Action<TValue> _onRemove; | |||
public AsyncCache(Func<string, string, TValue> onCreate, Action<TValue, TModel> onUpdate, Action<TValue> onRemove) | |||
public AsyncCache(Func<string, string, TValue> onCreate, Action<TValue, TModel> onUpdate, Action<TValue> onRemove = null) | |||
{ | |||
_dictionary = new ConcurrentDictionary<string, TValue>(); | |||
_onCreate = onCreate; | |||
@@ -49,7 +49,8 @@ namespace Discord.Helpers | |||
isNew = !_dictionary.TryGetValue(key, out value); | |||
if (isNew) | |||
value = _onCreate(key, parentKey); | |||
_onUpdate(value, model); | |||
if (model != null) | |||
_onUpdate(value, model); | |||
if (isNew) | |||
{ | |||
//If this fails, repeat as an update instead of an add | |||
@@ -68,7 +69,11 @@ namespace Discord.Helpers | |||
{ | |||
TValue value = null; | |||
if (_dictionary.TryRemove(key, out value)) | |||
{ | |||
if (_onRemove != null) | |||
_onRemove(value); | |||
return value; | |||
} | |||
else | |||
return null; | |||
} | |||
@@ -10,7 +10,6 @@ using System.Globalization; | |||
#if TEST_RESPONSES | |||
using System.Diagnostics; | |||
using JsonErrorEventArgs = Newtonsoft.Json.Serialization.ErrorEventArgs; | |||
#endif | |||
namespace Discord.Helpers | |||
@@ -18,6 +18,11 @@ namespace Discord | |||
public bool IsSuppressed { get; internal set; } | |||
public string SessionId { get; internal set; } | |||
public string Token { get; internal set; } | |||
/// <summary> Returns the id for the game this user is currently playing. </summary> | |||
public string GameId { get; internal set; } | |||
/// <summary> Returns the current status for this user. </summary> | |||
public string Status { get; internal set; } | |||
public string ServerId { get; } | |||
public Server Server => _client.GetServer(ServerId); | |||
@@ -31,29 +36,11 @@ namespace Discord | |||
public string[] RoleIds { get; internal set; } | |||
public IEnumerable<Role> Roles => RoleIds.Select(x => _client.GetRole(x)); | |||
public Membership(string serverId, string userId, DateTime joinedAt, DiscordClient client) | |||
public Membership(string serverId, string userId, DiscordClient client) | |||
{ | |||
ServerId = serverId; | |||
UserId = userId; | |||
JoinedAt = joinedAt; | |||
_client = client; | |||
} | |||
internal void Update(ExtendedServerInfo.Membership data) | |||
{ | |||
IsDeafened = data.IsDeaf; | |||
IsMuted = data.IsMuted; | |||
RoleIds = data.Roles; | |||
} | |||
internal void Update(WebSocketEvents.VoiceStateUpdate data) | |||
{ | |||
VoiceChannelId = data.ChannelId; | |||
IsDeafened = data.IsDeafened; | |||
IsMuted = data.IsMuted; | |||
IsSelfDeafened = data.IsSelfDeafened; | |||
IsSelfMuted = data.IsSelfMuted; | |||
IsSuppressed = data.IsSuppressed; | |||
SessionId = data.SessionId; | |||
} | |||
} | |||
} |
@@ -7,10 +7,39 @@ namespace Discord | |||
{ | |||
public sealed class Message | |||
{ | |||
public class Attachment | |||
public sealed class Attachment : File | |||
{ | |||
/// <summary> Unique identifier for this file. </summary> | |||
public string Id { get; internal set; } | |||
/// <summary> Size, in bytes, of this file file. </summary> | |||
public int Size { get; internal set; } | |||
/// <summary> Filename of this file. </summary> | |||
public string Filename { get; internal set; } | |||
} | |||
public sealed class Embed | |||
{ | |||
/// <summary> URL of this embed. </summary> | |||
public string Url { get; internal set; } | |||
/// <summary> Type of this embed. </summary> | |||
public string Type { get; internal set; } | |||
/// <summary> Title for this embed. </summary> | |||
public string Title { get; internal set; } | |||
/// <summary> Summary of this embed. </summary> | |||
public string Description { get; internal set; } | |||
/// <summary> Returns information about the providing website of this embed. </summary> | |||
public EmbedProvider Provider { get; internal set; } | |||
/// <summary> Returns the thumbnail of this embed. </summary> | |||
public File Thumbnail { get; internal set; } | |||
} | |||
public sealed class EmbedProvider | |||
{ | |||
/// <summary> URL of this embed provider. </summary> | |||
public string Url { get; internal set; } | |||
/// <summary> Name of this embed provider. </summary> | |||
public string Name { get; internal set; } | |||
} | |||
public class File | |||
{ | |||
/// <summary> Download url for this file. </summary> | |||
public string Url { get; internal set; } | |||
/// <summary> Preview url for this file. </summary> | |||
@@ -19,10 +48,6 @@ namespace Discord | |||
public int? Width { get; internal set; } | |||
/// <summary> Height of this file, if it is an image. </summary> | |||
public int? Height { get; internal set; } | |||
/// <summary> Size, in bytes, of this file file. </summary> | |||
public int Size { get; internal set; } | |||
/// <summary> Filename of this file. </summary> | |||
public string Filename { get; internal set; } | |||
} | |||
private readonly DiscordClient _client; | |||
@@ -49,6 +74,9 @@ namespace Discord | |||
public DateTime? EditedTimestamp { get; internal set; } | |||
/// <summary> Returns the attachments included in this message. </summary> | |||
public Attachment[] Attachments { get; internal set; } | |||
//TODO: Not Implemented | |||
/// <summary> Returns a collection of all embeded content in this message. </summary> | |||
public Embed[] Embeds { get; internal set; } | |||
/// <summary> Returns a collection of all user ids mentioned in this message. </summary> | |||
public string[] MentionIds { get; internal set; } | |||
@@ -68,10 +96,6 @@ namespace Discord | |||
[JsonIgnore] | |||
public User User => _client.GetUser(UserId); | |||
//TODO: Not Implemented | |||
/// <summary> Not implemented, stored for reference. </summary> | |||
public object[] Embeds { get; internal set; } | |||
internal Message(string id, string channelId, DiscordClient client) | |||
{ | |||
Id = id; | |||
@@ -0,0 +1,67 @@ | |||
namespace Discord | |||
{ | |||
public sealed class PackedPermissions | |||
{ | |||
private uint _rawValue; | |||
internal uint RawValue { get { return _rawValue; } set { _rawValue = value; } } | |||
internal PackedPermissions() { } | |||
internal PackedPermissions(uint rawValue) { _rawValue = rawValue; } | |||
/// <summary> If True, a user may create invites. </summary> | |||
public bool General_CreateInstantInvite => ((_rawValue >> 0) & 0x1) == 1; | |||
/// <summary> If True, a user may ban users from the server. </summary> | |||
public bool General_BanMembers => ((_rawValue >> 1) & 0x1) == 1; | |||
/// <summary> If True, a user may kick users from the server. </summary> | |||
public bool General_KickMembers => ((_rawValue >> 2) & 0x1) == 1; | |||
/// <summary> If True, a user adjust roles. </summary> | |||
/// <remarks> Having this permission effectively gives all the others as a user may add them to themselves. </remarks> | |||
public bool General_ManageRoles => ((_rawValue >> 3) & 0x1) == 1; | |||
/// <summary> If True, a user may create, delete and modify channels. </summary> | |||
public bool General_ManageChannels => ((_rawValue >> 4) & 0x1) == 1; | |||
/// <summary> If True, a user may adjust server properties. </summary> | |||
public bool General_ManageServer => ((_rawValue >> 5) & 0x1) == 1; | |||
//4 Unused | |||
/// <summary> If True, a user may join channels. </summary> | |||
/// <remarks> Note that without this permission, a channel is not sent by the server. </remarks> | |||
public bool Text_ReadMessages => ((_rawValue >> 10) & 0x1) == 1; | |||
/// <summary> If True, a user may send messages. </summary> | |||
public bool Text_SendMessages => ((_rawValue >> 11) & 0x1) == 1; | |||
/// <summary> If True, a user may send text-to-speech messages. </summary> | |||
public bool Text_SendTTSMessages => ((_rawValue >> 12) & 0x1) == 1; | |||
/// <summary> If True, a user may delete messages. </summary> | |||
public bool Text_ManageMessages => ((_rawValue >> 13) & 0x1) == 1; | |||
/// <summary> If True, Discord will auto-embed links sent by this user. </summary> | |||
public bool Text_EmbedLinks => ((_rawValue >> 14) & 0x1) == 1; | |||
/// <summary> If True, a user may send files. </summary> | |||
public bool Text_AttachFiles => ((_rawValue >> 15) & 0x1) == 1; | |||
/// <summary> If True, a user may read previous messages. </summary> | |||
public bool Text_ReadMessageHistory => ((_rawValue >> 16) & 0x1) == 1; | |||
/// <summary> If True, a user may mention @everyone. </summary> | |||
public bool Text_MentionEveryone => ((_rawValue >> 17) & 0x1) == 1; | |||
//2 Unused | |||
/// <summary> If True, a user may connect to a voice channel. </summary> | |||
public bool Voice_Connect => ((_rawValue >> 20) & 0x1) == 1; | |||
/// <summary> If True, a user may speak in a voice channel. </summary> | |||
public bool Voice_Speak => ((_rawValue >> 21) & 0x1) == 1; | |||
/// <summary> If True, a user may mute users. </summary> | |||
public bool Voice_MuteMembers => ((_rawValue >> 22) & 0x1) == 1; | |||
/// <summary> If True, a user may deafen users. </summary> | |||
public bool Voice_DeafenMembers => ((_rawValue >> 23) & 0x1) == 1; | |||
/// <summary> If True, a user may move other users between voice channels. </summary> | |||
public bool Voice_MoveMembers => ((_rawValue >> 24) & 0x1) == 1; | |||
/// <summary> If True, a user may use voice activation rather than push-to-talk. </summary> | |||
public bool Voice_UseVoiceActivation => ((_rawValue >> 25) & 0x1) == 1; | |||
//6 Unused | |||
public static implicit operator uint (PackedPermissions perms) | |||
{ | |||
return perms._rawValue; | |||
} | |||
} | |||
} |
@@ -4,70 +4,6 @@ namespace Discord | |||
{ | |||
public sealed class Role | |||
{ | |||
public sealed class PackedPermissions | |||
{ | |||
private uint _rawValue; | |||
internal uint RawValue { get { return _rawValue; } set { _rawValue = value; } } | |||
internal PackedPermissions() { } | |||
/// <summary> If True, a user may create invites. </summary> | |||
public bool General_CreateInstantInvite => ((_rawValue >> 0) & 0x1) == 1; | |||
/// <summary> If True, a user may ban users from the server. </summary> | |||
public bool General_BanMembers => ((_rawValue >> 1) & 0x1) == 1; | |||
/// <summary> If True, a user may kick users from the server. </summary> | |||
public bool General_KickMembers => ((_rawValue >> 2) & 0x1) == 1; | |||
/// <summary> If True, a user adjust roles. </summary> | |||
/// <remarks> Having this permission effectively gives all the others as a user may add them to themselves. </remarks> | |||
public bool General_ManageRoles => ((_rawValue >> 3) & 0x1) == 1; | |||
/// <summary> If True, a user may create, delete and modify channels. </summary> | |||
public bool General_ManageChannels => ((_rawValue >> 4) & 0x1) == 1; | |||
/// <summary> If True, a user may adjust server properties. </summary> | |||
public bool General_ManageServer => ((_rawValue >> 5) & 0x1) == 1; | |||
//4 Unused | |||
/// <summary> If True, a user may join channels. </summary> | |||
/// <remarks> Note that without this permission, a channel is not sent by the server. </remarks> | |||
public bool Text_ReadMessages => ((_rawValue >> 10) & 0x1) == 1; | |||
/// <summary> If True, a user may send messages. </summary> | |||
public bool Text_SendMessages => ((_rawValue >> 11) & 0x1) == 1; | |||
/// <summary> If True, a user may send text-to-speech messages. </summary> | |||
public bool Text_SendTTSMessages => ((_rawValue >> 12) & 0x1) == 1; | |||
/// <summary> If True, a user may delete messages. </summary> | |||
public bool Text_ManageMessages => ((_rawValue >> 13) & 0x1) == 1; | |||
/// <summary> If True, Discord will auto-embed links sent by this user. </summary> | |||
public bool Text_EmbedLinks => ((_rawValue >> 14) & 0x1) == 1; | |||
/// <summary> If True, a user may send files. </summary> | |||
public bool Text_AttachFiles => ((_rawValue >> 15) & 0x1) == 1; | |||
/// <summary> If True, a user may read previous messages. </summary> | |||
public bool Text_ReadMessageHistory => ((_rawValue >> 16) & 0x1) == 1; | |||
/// <summary> If True, a user may mention @everyone. </summary> | |||
public bool Text_MentionEveryone => ((_rawValue >> 17) & 0x1) == 1; | |||
//2 Unused | |||
/// <summary> If True, a user may connect to a voice channel. </summary> | |||
public bool Voice_Connect => ((_rawValue >> 20) & 0x1) == 1; | |||
/// <summary> If True, a user may speak in a voice channel. </summary> | |||
public bool Voice_Speak => ((_rawValue >> 21) & 0x1) == 1; | |||
/// <summary> If True, a user may mute users. </summary> | |||
public bool Voice_MuteMembers => ((_rawValue >> 22) & 0x1) == 1; | |||
/// <summary> If True, a user may deafen users. </summary> | |||
public bool Voice_DeafenMembers => ((_rawValue >> 23) & 0x1) == 1; | |||
/// <summary> If True, a user may move other users between voice channels. </summary> | |||
public bool Voice_MoveMembers => ((_rawValue >> 24) & 0x1) == 1; | |||
/// <summary> If True, a user may use voice activation rather than push-to-talk. </summary> | |||
public bool Voice_UseVoiceActivation => ((_rawValue >> 25) & 0x1) == 1; | |||
//6 Unused | |||
public static implicit operator uint(PackedPermissions perms) | |||
{ | |||
return perms._rawValue; | |||
} | |||
} | |||
private readonly DiscordClient _client; | |||
/// <summary> Returns the unique identifier for this role. </summary> | |||
@@ -1,4 +1,5 @@ | |||
using System; | |||
using Discord.Helpers; | |||
using System; | |||
using System.Collections.Concurrent; | |||
using System.Collections.Generic; | |||
using System.Linq; | |||
@@ -38,9 +39,9 @@ namespace Discord | |||
/// <summary> Returns the default channel for this server. </summary> | |||
public Channel DefaultChannel =>_client.GetChannel(DefaultChannelId); | |||
internal ConcurrentDictionary<string, Membership> _members; | |||
internal AsyncCache<Membership, API.Models.MemberInfo> _members; | |||
/// <summary> Returns a collection of all channels within this server. </summary> | |||
public IEnumerable<Membership> Members => _members.Values; | |||
public IEnumerable<Membership> Members => _members; | |||
internal ConcurrentDictionary<string, bool> _bans; | |||
/// <summary> Returns a collection of all users banned on this server. </summary> | |||
@@ -54,38 +55,59 @@ namespace Discord | |||
/// <summary> Returns a collection of all roles within this server. </summary> | |||
public IEnumerable<Role> Roles => _client.Roles.Where(x => x.ServerId == Id); | |||
//TODO: Not Implemented | |||
/// <summary> Not implemented, stored for reference. </summary> | |||
public object Presence { get; internal set; } | |||
//TODO: Not Implemented | |||
/// <summary> Not implemented, stored for reference. </summary> | |||
public object[] VoiceStates { get; internal set; } | |||
internal Server(string id, DiscordClient client) | |||
{ | |||
Id = id; | |||
_client = client; | |||
_members = new ConcurrentDictionary<string, Membership>(); | |||
_bans = new ConcurrentDictionary<string, bool>(); | |||
_members = new AsyncCache<Membership, API.Models.MemberInfo>( | |||
(key, parentKey) => new Membership(parentKey, key, _client), | |||
(member, model) => | |||
{ | |||
if (model is API.Models.PresenceMemberInfo) | |||
{ | |||
var extendedModel = model as API.Models.PresenceMemberInfo; | |||
member.Status = extendedModel.Status; | |||
member.GameId = extendedModel.GameId; | |||
} | |||
if (model is API.Models.VoiceMemberInfo) | |||
{ | |||
var extendedModel = model as API.Models.VoiceMemberInfo; | |||
member.VoiceChannelId = extendedModel.ChannelId; | |||
member.IsDeafened = extendedModel.IsDeafened; | |||
member.IsMuted = extendedModel.IsMuted; | |||
member.IsSelfDeafened = extendedModel.IsSelfDeafened; | |||
member.IsSelfMuted = extendedModel.IsSelfMuted; | |||
member.IsSuppressed = extendedModel.IsSuppressed; | |||
member.SessionId = extendedModel.SessionId; | |||
member.Token = extendedModel.Token; | |||
} | |||
if (model is API.Models.RoleMemberInfo) | |||
{ | |||
var extendedModel = model as API.Models.RoleMemberInfo; | |||
member.IsDeafened = extendedModel.IsDeafened; | |||
member.IsMuted = extendedModel.IsMuted; | |||
member.RoleIds = extendedModel.Roles; | |||
if (extendedModel.JoinedAt.HasValue) | |||
member.JoinedAt = extendedModel.JoinedAt.Value; | |||
} | |||
} | |||
); | |||
} | |||
internal void AddMember(Membership membership) | |||
internal Membership UpdateMember(API.Models.MemberInfo membership) | |||
{ | |||
_members[membership.UserId] = membership; | |||
return _members.Update(membership.User?.Id ?? membership.UserId, Id, membership); | |||
} | |||
internal Membership RemoveMember(string userId) | |||
{ | |||
Membership result = null; | |||
_members.TryRemove(userId, out result); | |||
return result; | |||
return _members.Remove(userId); | |||
} | |||
public Membership GetMembership(User user) | |||
=> GetMember(user.Id); | |||
public Membership GetMember(string userId) | |||
{ | |||
Membership result = null; | |||
_members.TryGetValue(userId, out result); | |||
return result; | |||
return _members[userId]; | |||
} | |||
internal void AddBan(string banId) | |||
@@ -27,10 +27,6 @@ namespace Discord | |||
/// <summary> Returns if the email for this user has been verified. </summary> | |||
/// <remarks> This field is only ever populated for the current logged in user. </remarks> | |||
public bool IsVerified { get; internal set; } | |||
/// <summary> Returns the id for the game this user is currently playing. </summary> | |||
public string GameId { get; internal set; } | |||
/// <summary> Returns the current status for this user. </summary> | |||
public string Status { get; internal set; } | |||
/// <summary> Returns the string "<@Id>" to be used as a shortcut when including mentions in text. </summary> | |||
public string Mention { get { return $"<@{Id}>"; } } | |||