Browse Source

Updated to use the v2 API. Added Embeds, Channel Permissions and Voice State parsing. Moved Presence from User to Member.

tags/docs-0.9
Brandon Smith 9 years ago
parent
commit
285deb48ef
16 changed files with 451 additions and 289 deletions
  1. +3
    -0
      src/Discord.Net.Net45/Discord.Net.csproj
  2. +95
    -22
      src/Discord.Net/API/Models/Common.cs
  3. +2
    -0
      src/Discord.Net/API/Models/WebSocketCommands.cs
  4. +20
    -45
      src/Discord.Net/API/Models/WebSocketEvents.cs
  5. +14
    -5
      src/Discord.Net/Channel.cs
  6. +6
    -6
      src/Discord.Net/DiscordClient.Events.cs
  7. +154
    -87
      src/Discord.Net/DiscordClient.cs
  8. +3
    -6
      src/Discord.Net/DiscordWebSocket.cs
  9. +7
    -2
      src/Discord.Net/Helpers/AsyncCache.cs
  10. +0
    -1
      src/Discord.Net/Helpers/Http.cs
  11. +6
    -19
      src/Discord.Net/Membership.cs
  12. +33
    -9
      src/Discord.Net/Message.cs
  13. +67
    -0
      src/Discord.Net/PackedPermissions.cs
  14. +0
    -64
      src/Discord.Net/Role.cs
  15. +41
    -19
      src/Discord.Net/Server.cs
  16. +0
    -4
      src/Discord.Net/User.cs

+ 3
- 0
src/Discord.Net.Net45/Discord.Net.csproj View File

@@ -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>


+ 95
- 22
src/Discord.Net/API/Models/Common.cs View File

@@ -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")]


+ 2
- 0
src/Discord.Net/API/Models/WebSocketCommands.cs View File

@@ -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>();
}


+ 20
- 45
src/Discord.Net/API/Models/WebSocketEvents.cs View File

@@ -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 { }


+ 14
- 5
src/Discord.Net/Channel.cs View File

@@ -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)
{


+ 6
- 6
src/Discord.Net/DiscordClient.Events.cs View File

@@ -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)


+ 154
- 87
src/Discord.Net/DiscordClient.cs View File

@@ -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":


+ 3
- 6
src/Discord.Net/DiscordWebSocket.cs View File

@@ -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;


+ 7
- 2
src/Discord.Net/Helpers/AsyncCache.cs View File

@@ -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;
}


+ 0
- 1
src/Discord.Net/Helpers/Http.cs View File

@@ -10,7 +10,6 @@ using System.Globalization;

#if TEST_RESPONSES
using System.Diagnostics;
using JsonErrorEventArgs = Newtonsoft.Json.Serialization.ErrorEventArgs;
#endif

namespace Discord.Helpers


+ 6
- 19
src/Discord.Net/Membership.cs View File

@@ -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;
}
}
}

+ 33
- 9
src/Discord.Net/Message.cs View File

@@ -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;


+ 67
- 0
src/Discord.Net/PackedPermissions.cs View File

@@ -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;
}
}
}

+ 0
- 64
src/Discord.Net/Role.cs View File

@@ -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>


+ 41
- 19
src/Discord.Net/Server.cs View File

@@ -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)


+ 0
- 4
src/Discord.Net/User.cs View File

@@ -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 "&lt;@Id&gt;" to be used as a shortcut when including mentions in text. </summary>
public string Mention { get { return $"<@{Id}>"; } }


Loading…
Cancel
Save