| @@ -3,6 +3,7 @@ | |||||
| internal static class Endpoints | internal static class Endpoints | ||||
| { | { | ||||
| public static readonly string BaseUrl = "discordapp.com"; | public static readonly string BaseUrl = "discordapp.com"; | ||||
| public static readonly string BaseShortUrl = "discord.gg"; | |||||
| public static readonly string BaseHttps = $"https://{BaseUrl}"; | public static readonly string BaseHttps = $"https://{BaseUrl}"; | ||||
| // /api | // /api | ||||
| @@ -49,5 +50,8 @@ | |||||
| //Web Sockets | //Web Sockets | ||||
| public static readonly string BaseWss = "wss://" + BaseUrl; | public static readonly string BaseWss = "wss://" + BaseUrl; | ||||
| public static readonly string WebSocket_Hub = $"{BaseWss}/hub"; | public static readonly string WebSocket_Hub = $"{BaseWss}/hub"; | ||||
| //Website | |||||
| public static string InviteUrl(string code) => $"{BaseShortUrl}/{code}"; | |||||
| } | } | ||||
| } | } | ||||
| @@ -117,7 +117,7 @@ namespace Discord.API.Models | |||||
| public class Membership | public class Membership | ||||
| { | { | ||||
| [JsonProperty(PropertyName = "roles")] | [JsonProperty(PropertyName = "roles")] | ||||
| public object[] Roles; | |||||
| public string[] Roles; | |||||
| [JsonProperty(PropertyName = "mute")] | [JsonProperty(PropertyName = "mute")] | ||||
| public bool IsMuted; | public bool IsMuted; | ||||
| [JsonProperty(PropertyName = "deaf")] | [JsonProperty(PropertyName = "deaf")] | ||||
| @@ -48,12 +48,12 @@ namespace Discord.API.Models | |||||
| [JsonProperty(PropertyName = "joined_at")] | [JsonProperty(PropertyName = "joined_at")] | ||||
| public DateTime JoinedAt; | public DateTime JoinedAt; | ||||
| [JsonProperty(PropertyName = "roles")] | [JsonProperty(PropertyName = "roles")] | ||||
| public object[] Roles; | |||||
| public string[] Roles; | |||||
| } | } | ||||
| public sealed class GuildMemberUpdate : GuildMemberEvent | public sealed class GuildMemberUpdate : GuildMemberEvent | ||||
| { | { | ||||
| [JsonProperty(PropertyName = "roles")] | [JsonProperty(PropertyName = "roles")] | ||||
| public object[] Roles; | |||||
| public string[] Roles; | |||||
| } | } | ||||
| public sealed class GuildMemberRemove : GuildMemberEvent { } | public sealed class GuildMemberRemove : GuildMemberEvent { } | ||||
| @@ -55,6 +55,7 @@ | |||||
| <Compile Include="ChannelTypes.cs" /> | <Compile Include="ChannelTypes.cs" /> | ||||
| <Compile Include="Helpers\AsyncCache.cs" /> | <Compile Include="Helpers\AsyncCache.cs" /> | ||||
| <Compile Include="Invite.cs" /> | <Compile Include="Invite.cs" /> | ||||
| <Compile Include="Membership.cs" /> | |||||
| <Compile Include="Role.cs" /> | <Compile Include="Role.cs" /> | ||||
| <Compile Include="Message.cs" /> | <Compile Include="Message.cs" /> | ||||
| <Compile Include="Channel.cs" /> | <Compile Include="Channel.cs" /> | ||||
| @@ -176,32 +176,27 @@ namespace Discord | |||||
| //Member | //Member | ||||
| public sealed class MemberEventArgs : EventArgs | public sealed class MemberEventArgs : EventArgs | ||||
| { | { | ||||
| public readonly User User; | |||||
| public readonly Server Server; | |||||
| internal MemberEventArgs(User user, Server server) | |||||
| { | |||||
| User = user; | |||||
| Server = server; | |||||
| } | |||||
| public readonly Membership Membership; | |||||
| internal MemberEventArgs(Membership membership) { Membership = membership; } | |||||
| } | } | ||||
| public event EventHandler<MemberEventArgs> MemberAdded; | public event EventHandler<MemberEventArgs> MemberAdded; | ||||
| private void RaiseMemberAdded(User user, Server server) | |||||
| private void RaiseMemberAdded(Membership membership, Server server) | |||||
| { | { | ||||
| if (MemberAdded != null) | if (MemberAdded != null) | ||||
| MemberAdded(this, new MemberEventArgs(user, server)); | |||||
| MemberAdded(this, new MemberEventArgs(membership)); | |||||
| } | } | ||||
| public event EventHandler<MemberEventArgs> MemberRemoved; | public event EventHandler<MemberEventArgs> MemberRemoved; | ||||
| private void RaiseMemberRemoved(User user, Server server) | |||||
| private void RaiseMemberRemoved(Membership membership, Server server) | |||||
| { | { | ||||
| if (MemberRemoved != null) | if (MemberRemoved != null) | ||||
| MemberRemoved(this, new MemberEventArgs(user, server)); | |||||
| MemberRemoved(this, new MemberEventArgs(membership)); | |||||
| } | } | ||||
| public event EventHandler<MemberEventArgs> MemberUpdated; | public event EventHandler<MemberEventArgs> MemberUpdated; | ||||
| private void RaiseMemberUpdated(User user, Server server) | |||||
| private void RaiseMemberUpdated(Membership membership, Server server) | |||||
| { | { | ||||
| if (MemberUpdated != null) | if (MemberUpdated != null) | ||||
| MemberUpdated(this, new MemberEventArgs(user, server)); | |||||
| MemberUpdated(this, new MemberEventArgs(membership)); | |||||
| } | } | ||||
| //Status | //Status | ||||
| @@ -71,7 +71,7 @@ namespace Discord | |||||
| foreach (var channel in extendedModel.Channels) | foreach (var channel in extendedModel.Channels) | ||||
| { | { | ||||
| _channels.Update(channel.Id, model.Id, channel); | _channels.Update(channel.Id, model.Id, channel); | ||||
| if (channel.Type == ChannelTypes.Text) | |||||
| /*if (channel.Type == ChannelTypes.Text) | |||||
| { | { | ||||
| try | try | ||||
| { | { | ||||
| @@ -84,12 +84,12 @@ namespace Discord | |||||
| } | } | ||||
| } | } | ||||
| catch { } //Bad Permissions? | catch { } //Bad Permissions? | ||||
| } | |||||
| }*/ | |||||
| } | } | ||||
| foreach (var membership in extendedModel.Members) | foreach (var membership in extendedModel.Members) | ||||
| { | { | ||||
| _users.Update(membership.User.Id, membership.User); | _users.Update(membership.User.Id, membership.User); | ||||
| server.AddMember(membership.User.Id); | |||||
| server.AddMember(new Membership(server.Id, membership.User.Id, membership.JoinedAt, this) { RoleIds = membership.Roles, IsMuted = membership.IsMuted, IsDeafened = membership.IsDeaf }); | |||||
| } | } | ||||
| } | } | ||||
| }, | }, | ||||
| @@ -118,12 +118,13 @@ namespace Discord | |||||
| { | { | ||||
| var extendedModel = model as API.Models.Message; | var extendedModel = model as API.Models.Message; | ||||
| message.Attachments = extendedModel.Attachments; | message.Attachments = extendedModel.Attachments; | ||||
| message.Text = extendedModel.Content; | |||||
| message.Embeds = extendedModel.Embeds; | message.Embeds = extendedModel.Embeds; | ||||
| message.IsMentioningEveryone = extendedModel.IsMentioningEveryone; | message.IsMentioningEveryone = extendedModel.IsMentioningEveryone; | ||||
| message.IsTTS = extendedModel.IsTextToSpeech; | message.IsTTS = extendedModel.IsTextToSpeech; | ||||
| message.UserId = extendedModel.Author.Id; | |||||
| message.MentionIds = extendedModel.Mentions.Select(x => x.Id).ToArray(); | |||||
| message.UserId = extendedModel.Author.Id; | |||||
| message.Timestamp = extendedModel.Timestamp; | message.Timestamp = extendedModel.Timestamp; | ||||
| message.Text = extendedModel.Content; | |||||
| } | } | ||||
| if (model is WebSocketEvents.MessageUpdate) | if (model is WebSocketEvents.MessageUpdate) | ||||
| { | { | ||||
| @@ -137,8 +138,9 @@ namespace Discord | |||||
| (key, parentKey) => new Role(key, parentKey, this), | (key, parentKey) => new Role(key, parentKey, this), | ||||
| (role, model) => | (role, model) => | ||||
| { | { | ||||
| role.Name = model.Name; | |||||
| role.Permissions = model.Permissions; | role.Permissions = model.Permissions; | ||||
| }, | |||||
| }, | |||||
| role => { } | role => { } | ||||
| ); | ); | ||||
| _users = new AsyncCache<User, API.Models.UserReference>( | _users = new AsyncCache<User, API.Models.UserReference>( | ||||
| @@ -153,13 +155,13 @@ namespace Discord | |||||
| var extendedModel = model as SelfUserInfo; | var extendedModel = model as SelfUserInfo; | ||||
| user.Email = extendedModel.Email; | user.Email = extendedModel.Email; | ||||
| user.IsVerified = extendedModel.IsVerified; | user.IsVerified = extendedModel.IsVerified; | ||||
| } | |||||
| } | |||||
| if (model is PresenceUserInfo) | if (model is PresenceUserInfo) | ||||
| { | { | ||||
| var extendedModel = model as PresenceUserInfo; | var extendedModel = model as PresenceUserInfo; | ||||
| user.GameId = extendedModel.GameId; | user.GameId = extendedModel.GameId; | ||||
| user.Status = extendedModel.Status; | user.Status = extendedModel.Status; | ||||
| } | |||||
| } | |||||
| }, | }, | ||||
| user => { } | user => { } | ||||
| ); | ); | ||||
| @@ -261,8 +263,9 @@ namespace Discord | |||||
| var data = e.Event.ToObject<WebSocketEvents.GuildMemberAdd>(); | var data = e.Event.ToObject<WebSocketEvents.GuildMemberAdd>(); | ||||
| var user = _users.Update(data.User.Id, data.User); | var user = _users.Update(data.User.Id, data.User); | ||||
| var server = _servers[data.GuildId]; | var server = _servers[data.GuildId]; | ||||
| server._members[user.Id] = true; | |||||
| RaiseMemberAdded(user, server); | |||||
| var membership = new Membership(server.Id, data.User.Id, data.JoinedAt, this) { RoleIds = data.Roles }; | |||||
| server.AddMember(membership); | |||||
| RaiseMemberAdded(membership, server); | |||||
| } | } | ||||
| break; | break; | ||||
| case "GUILD_MEMBER_UPDATE": | case "GUILD_MEMBER_UPDATE": | ||||
| @@ -270,7 +273,10 @@ namespace Discord | |||||
| var data = e.Event.ToObject<WebSocketEvents.GuildMemberUpdate>(); | var data = e.Event.ToObject<WebSocketEvents.GuildMemberUpdate>(); | ||||
| var user = _users.Update(data.User.Id, data.User); | var user = _users.Update(data.User.Id, data.User); | ||||
| var server = _servers[data.GuildId]; | var server = _servers[data.GuildId]; | ||||
| RaiseMemberUpdated(user, server); | |||||
| var membership = server.GetMembership(data.User.Id); | |||||
| if (membership != null) | |||||
| membership.RoleIds = data.Roles; | |||||
| RaiseMemberUpdated(membership, server); | |||||
| } | } | ||||
| break; | break; | ||||
| case "GUILD_MEMBER_REMOVE": | case "GUILD_MEMBER_REMOVE": | ||||
| @@ -278,8 +284,12 @@ namespace Discord | |||||
| var data = e.Event.ToObject<WebSocketEvents.GuildMemberRemove>(); | var data = e.Event.ToObject<WebSocketEvents.GuildMemberRemove>(); | ||||
| var user = _users.Update(data.User.Id, data.User); | var user = _users.Update(data.User.Id, data.User); | ||||
| var server = _servers[data.GuildId]; | var server = _servers[data.GuildId]; | ||||
| if (server != null && server.RemoveMember(user.Id)) | |||||
| RaiseMemberRemoved(user, server); | |||||
| if (server != null) | |||||
| { | |||||
| var membership = server.RemoveMember(user.Id); | |||||
| if (membership != null) | |||||
| RaiseMemberRemoved(membership, server); | |||||
| } | |||||
| } | } | ||||
| break; | break; | ||||
| @@ -287,14 +297,14 @@ namespace Discord | |||||
| case "GUILD_ROLE_CREATE": | case "GUILD_ROLE_CREATE": | ||||
| { | { | ||||
| var data = e.Event.ToObject<WebSocketEvents.GuildRoleCreateUpdate>(); | var data = e.Event.ToObject<WebSocketEvents.GuildRoleCreateUpdate>(); | ||||
| var role = _roles.Update(data.Role.Id, data.Role); | |||||
| var role = _roles.Update(data.Role.Id, data.GuildId, data.Role); | |||||
| RaiseRoleCreated(role); | RaiseRoleCreated(role); | ||||
| } | } | ||||
| break; | break; | ||||
| case "GUILD_ROLE_UPDATE": | case "GUILD_ROLE_UPDATE": | ||||
| { | { | ||||
| var data = e.Event.ToObject<WebSocketEvents.GuildRoleCreateUpdate>(); | var data = e.Event.ToObject<WebSocketEvents.GuildRoleCreateUpdate>(); | ||||
| var role = _roles.Update(data.Role.Id, data.Role); | |||||
| var role = _roles.Update(data.Role.Id, data.GuildId, data.Role); | |||||
| RaiseRoleUpdated(role); | RaiseRoleUpdated(role); | ||||
| } | } | ||||
| break; | break; | ||||
| @@ -454,7 +464,9 @@ namespace Discord | |||||
| } | } | ||||
| public Role GetRole(string id) => _roles[id]; | public Role GetRole(string id) => _roles[id]; | ||||
| public Role FindRole(string name) | |||||
| public Role FindRole(Server server, string name) | |||||
| => FindRole(server.Id, name); | |||||
| public Role FindRole(string serverId, string name) | |||||
| { | { | ||||
| return _roles | return _roles | ||||
| .Where(x => string.Equals(x.Name, name, StringComparison.InvariantCultureIgnoreCase)) | .Where(x => string.Equals(x.Name, name, StringComparison.InvariantCultureIgnoreCase)) | ||||
| @@ -515,13 +527,13 @@ namespace Discord | |||||
| } | } | ||||
| //Channels | //Channels | ||||
| public Task<Channel> CreateChannel(Server server, string name, string region) | |||||
| => CreateChannel(server.Id, name, region); | |||||
| public async Task<Channel> CreateChannel(string serverId, string name, string region) | |||||
| public Task<Channel> CreateChannel(Server server, string name, string type) | |||||
| => CreateChannel(server.Id, name, type); | |||||
| public async Task<Channel> CreateChannel(string serverId, string name, string type) | |||||
| { | { | ||||
| CheckReady(); | CheckReady(); | ||||
| var response = await DiscordAPI.CreateChannel(serverId, name, region, _httpOptions); | |||||
| return _channels.Update(response.Id, response); | |||||
| var response = await DiscordAPI.CreateChannel(serverId, name, type, _httpOptions); | |||||
| return _channels.Update(response.Id, serverId, response); | |||||
| } | } | ||||
| public Task<Channel> CreatePMChannel(User user) | public Task<Channel> CreatePMChannel(User user) | ||||
| => CreatePMChannel(user.Id); | => CreatePMChannel(user.Id); | ||||
| @@ -10,6 +10,8 @@ namespace Discord | |||||
| public bool IsRevoked, IsTemporary; | public bool IsRevoked, IsTemporary; | ||||
| public readonly string Code, XkcdPass; | public readonly string Code, XkcdPass; | ||||
| public string Url { get { return API.Endpoints.InviteUrl(XkcdPass ?? Code); } } | |||||
| public string InviterId { get; internal set; } | public string InviterId { get; internal set; } | ||||
| [JsonIgnore] | [JsonIgnore] | ||||
| public User Inviter { get { return _client.GetUser(InviterId); } } | public User Inviter { get { return _client.GetUser(InviterId); } } | ||||
| @@ -0,0 +1,33 @@ | |||||
| using System; | |||||
| using System.Collections.Generic; | |||||
| using System.Linq; | |||||
| namespace Discord | |||||
| { | |||||
| public class Membership | |||||
| { | |||||
| private readonly DiscordClient _client; | |||||
| public DateTime JoinedAt; | |||||
| public bool IsMuted { get; internal set; } | |||||
| public bool IsDeafened { get; internal set; } | |||||
| public string ServerId { get; } | |||||
| public Server Server { get { return _client.GetServer(ServerId); } } | |||||
| public string UserId { get; } | |||||
| public User User { get { return _client.GetUser(UserId); } } | |||||
| public string[] RoleIds { get; internal set; } | |||||
| public IEnumerable<Role> Roles { get { return RoleIds.Select(x => _client.GetRole((string)x)); } } | |||||
| public Membership(string serverId, string userId, DateTime joinedAt, DiscordClient client) | |||||
| { | |||||
| ServerId = serverId; | |||||
| UserId = userId; | |||||
| _client = client; | |||||
| JoinedAt = joinedAt; | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -1,5 +1,7 @@ | |||||
| using Newtonsoft.Json; | using Newtonsoft.Json; | ||||
| using System; | using System; | ||||
| using System.Collections.Generic; | |||||
| using System.Linq; | |||||
| namespace Discord | namespace Discord | ||||
| { | { | ||||
| @@ -14,6 +16,10 @@ namespace Discord | |||||
| public string Text { get; internal set; } | public string Text { get; internal set; } | ||||
| public DateTime Timestamp { get; internal set; } | public DateTime Timestamp { get; internal set; } | ||||
| public string[] MentionIds { get; internal set; } | |||||
| [JsonIgnore] | |||||
| public IEnumerable<User> Mentions { get { return MentionIds.Select(x => _client.GetUser(x)).Where(x => x != null); } } | |||||
| public string ChannelId { get; } | public string ChannelId { get; } | ||||
| [JsonIgnore] | [JsonIgnore] | ||||
| public Channel Channel { get { return _client.GetChannel(ChannelId); } } | public Channel Channel { get { return _client.GetChannel(ChannelId); } } | ||||
| @@ -9,5 +9,5 @@ | |||||
| [assembly: AssemblyTrademark("")] | [assembly: AssemblyTrademark("")] | ||||
| [assembly: AssemblyCulture("")] | [assembly: AssemblyCulture("")] | ||||
| [assembly: AssemblyVersion("0.3.0.0")] | |||||
| [assembly: AssemblyFileVersion("0.3.0.0")] | |||||
| [assembly: AssemblyVersion("0.3.1.0")] | |||||
| [assembly: AssemblyFileVersion("0.3.1.0")] | |||||
| @@ -18,6 +18,7 @@ namespace Discord | |||||
| internal Role(string id, string serverId, DiscordClient client) | internal Role(string id, string serverId, DiscordClient client) | ||||
| { | { | ||||
| Id = id; | Id = id; | ||||
| ServerId = serverId; | |||||
| _client = client; | _client = client; | ||||
| } | } | ||||
| @@ -24,8 +24,8 @@ namespace Discord | |||||
| public string DefaultChannelId { get { return Id; } } | public string DefaultChannelId { get { return Id; } } | ||||
| public Channel DefaultChannel { get { return _client.GetChannel(DefaultChannelId); } } | public Channel DefaultChannel { get { return _client.GetChannel(DefaultChannelId); } } | ||||
| internal ConcurrentDictionary<string, bool> _members; | |||||
| public IEnumerable<User> Members { get { return _members.Keys.Select(x => _client.GetUser(x)); } } | |||||
| internal ConcurrentDictionary<string, Membership> _members; | |||||
| public IEnumerable<Membership> Members { get { return _members.Values; } } | |||||
| internal ConcurrentDictionary<string, bool> _bans; | internal ConcurrentDictionary<string, bool> _bans; | ||||
| public IEnumerable<User> Bans { get { return _bans.Keys.Select(x => _client.GetUser(x)); } } | public IEnumerable<User> Bans { get { return _bans.Keys.Select(x => _client.GetUser(x)); } } | ||||
| @@ -41,7 +41,7 @@ namespace Discord | |||||
| { | { | ||||
| Id = id; | Id = id; | ||||
| _client = client; | _client = client; | ||||
| _members = new ConcurrentDictionary<string, bool>(); | |||||
| _members = new ConcurrentDictionary<string, Membership>(); | |||||
| _bans = new ConcurrentDictionary<string, bool>(); | _bans = new ConcurrentDictionary<string, bool>(); | ||||
| } | } | ||||
| @@ -50,24 +50,33 @@ namespace Discord | |||||
| return Name; | return Name; | ||||
| } | } | ||||
| internal void AddMember(string id) | |||||
| internal void AddMember(Membership membership) | |||||
| { | { | ||||
| _members.TryAdd(id, true); | |||||
| _members[membership.UserId] = membership; | |||||
| } | } | ||||
| internal bool RemoveMember(string id) | |||||
| internal Membership RemoveMember(string userId) | |||||
| { | { | ||||
| bool ignored; | |||||
| return _members.TryRemove(id, out ignored); | |||||
| Membership result = null; | |||||
| _members.TryRemove(userId, out result); | |||||
| return result; | |||||
| } | |||||
| public Membership GetMembership(User user) | |||||
| => GetMembership(user.Id); | |||||
| public Membership GetMembership(string userId) | |||||
| { | |||||
| Membership result = null; | |||||
| _members.TryGetValue(userId, out result); | |||||
| return result; | |||||
| } | } | ||||
| internal void AddBan(string id) | |||||
| internal void AddBan(string banId) | |||||
| { | { | ||||
| _bans.TryAdd(id, true); | |||||
| _bans.TryAdd(banId, true); | |||||
| } | } | ||||
| internal bool RemoveBan(string id) | |||||
| internal bool RemoveBan(string banId) | |||||
| { | { | ||||
| bool ignored; | bool ignored; | ||||
| return _bans.TryRemove(id, out ignored); | |||||
| return _bans.TryRemove(banId, out ignored); | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||