| @@ -37,6 +37,7 @@ | |||||
| <Private>True</Private> | <Private>True</Private> | ||||
| </Reference> | </Reference> | ||||
| <Reference Include="System" /> | <Reference Include="System" /> | ||||
| <Reference Include="System.Net.Http" /> | |||||
| </ItemGroup> | </ItemGroup> | ||||
| <ItemGroup> | <ItemGroup> | ||||
| <None Include="packages.config" /> | <None Include="packages.config" /> | ||||
| @@ -9,109 +9,108 @@ namespace Discord.API | |||||
| public const int MaxMessageSize = 2000; | public const int MaxMessageSize = 2000; | ||||
| //Auth | //Auth | ||||
| public static async Task<APIResponses.AuthRegister> LoginAnonymous(string username, HttpOptions options) | |||||
| public static async Task<APIResponses.AuthRegister> LoginAnonymous(string username) | |||||
| { | { | ||||
| var fingerprintResponse = await Http.Post<APIResponses.AuthFingerprint>(Endpoints.AuthFingerprint, options); | |||||
| var fingerprintResponse = await Http.Post<APIResponses.AuthFingerprint>(Endpoints.AuthFingerprint); | |||||
| var registerRequest = new APIRequests.AuthRegisterRequest { Fingerprint = fingerprintResponse.Fingerprint, Username = username }; | var registerRequest = new APIRequests.AuthRegisterRequest { Fingerprint = fingerprintResponse.Fingerprint, Username = username }; | ||||
| var registerResponse = await Http.Post<APIResponses.AuthRegister>(Endpoints.AuthRegister, registerRequest, options); | |||||
| var registerResponse = await Http.Post<APIResponses.AuthRegister>(Endpoints.AuthRegister, registerRequest); | |||||
| return registerResponse; | return registerResponse; | ||||
| } | } | ||||
| public static async Task<APIResponses.AuthLogin> Login(string email, string password, HttpOptions options) | |||||
| public static async Task<APIResponses.AuthLogin> Login(string email, string password) | |||||
| { | { | ||||
| var request = new APIRequests.AuthLogin { Email = email, Password = password }; | var request = new APIRequests.AuthLogin { Email = email, Password = password }; | ||||
| var response = await Http.Post<APIResponses.AuthLogin>(Endpoints.AuthLogin, request, options); | |||||
| options.Token = response.Token; | |||||
| var response = await Http.Post<APIResponses.AuthLogin>(Endpoints.AuthLogin, request); | |||||
| return response; | return response; | ||||
| } | } | ||||
| public static Task Logout(HttpOptions options) | |||||
| => Http.Post(Endpoints.AuthLogout, options); | |||||
| public static Task Logout() | |||||
| => Http.Post(Endpoints.AuthLogout); | |||||
| //Servers | //Servers | ||||
| public static Task<APIResponses.CreateServer> CreateServer(string name, string region, HttpOptions options) | |||||
| public static Task<APIResponses.CreateServer> CreateServer(string name, string region) | |||||
| { | { | ||||
| var request = new APIRequests.CreateServer { Name = name, Region = region }; | var request = new APIRequests.CreateServer { Name = name, Region = region }; | ||||
| return Http.Post<APIResponses.CreateServer>(Endpoints.Servers, request, options); | |||||
| return Http.Post<APIResponses.CreateServer>(Endpoints.Servers, request); | |||||
| } | } | ||||
| public static Task LeaveServer(string id, HttpOptions options) | |||||
| => Http.Delete<APIResponses.DeleteServer>(Endpoints.Server(id), options); | |||||
| public static Task LeaveServer(string id) | |||||
| => Http.Delete<APIResponses.DeleteServer>(Endpoints.Server(id)); | |||||
| //Channels | //Channels | ||||
| public static Task<APIResponses.CreateChannel> CreateChannel(string serverId, string name, string channelType, HttpOptions options) | |||||
| public static Task<APIResponses.CreateChannel> CreateChannel(string serverId, string name, string channelType) | |||||
| { | { | ||||
| var request = new APIRequests.CreateChannel { Name = name, Type = channelType }; | var request = new APIRequests.CreateChannel { Name = name, Type = channelType }; | ||||
| return Http.Post<APIResponses.CreateChannel>(Endpoints.ServerChannels(serverId), request, options); | |||||
| return Http.Post<APIResponses.CreateChannel>(Endpoints.ServerChannels(serverId), request); | |||||
| } | } | ||||
| public static Task<APIResponses.CreateChannel> CreatePMChannel(string myId, string recipientId, HttpOptions options) | |||||
| public static Task<APIResponses.CreateChannel> CreatePMChannel(string myId, string recipientId) | |||||
| { | { | ||||
| var request = new APIRequests.CreatePMChannel { RecipientId = recipientId }; | var request = new APIRequests.CreatePMChannel { RecipientId = recipientId }; | ||||
| return Http.Post<APIResponses.CreateChannel>(Endpoints.UserChannels(myId), request, options); | |||||
| return Http.Post<APIResponses.CreateChannel>(Endpoints.UserChannels(myId), request); | |||||
| } | } | ||||
| public static Task<APIResponses.DestroyChannel> DestroyChannel(string channelId, HttpOptions options) | |||||
| => Http.Delete<APIResponses.DestroyChannel>(Endpoints.Channel(channelId), options); | |||||
| public static Task<APIResponses.GetMessages[]> GetMessages(string channelId, int count, HttpOptions options) | |||||
| => Http.Get<APIResponses.GetMessages[]>(Endpoints.ChannelMessages(channelId, count), options); | |||||
| public static Task<APIResponses.DestroyChannel> DestroyChannel(string channelId) | |||||
| => Http.Delete<APIResponses.DestroyChannel>(Endpoints.Channel(channelId)); | |||||
| public static Task<APIResponses.GetMessages[]> GetMessages(string channelId, int count) | |||||
| => Http.Get<APIResponses.GetMessages[]>(Endpoints.ChannelMessages(channelId, count)); | |||||
| //Members | //Members | ||||
| public static Task Kick(string serverId, string memberId, HttpOptions options) | |||||
| => Http.Delete(Endpoints.ServerMember(serverId, memberId), options); | |||||
| public static Task Ban(string serverId, string memberId, HttpOptions options) | |||||
| => Http.Put(Endpoints.ServerBan(serverId, memberId), options); | |||||
| public static Task Unban(string serverId, string memberId, HttpOptions options) | |||||
| => Http.Delete(Endpoints.ServerBan(serverId, memberId), options); | |||||
| public static Task Kick(string serverId, string memberId) | |||||
| => Http.Delete(Endpoints.ServerMember(serverId, memberId)); | |||||
| public static Task Ban(string serverId, string memberId) | |||||
| => Http.Put(Endpoints.ServerBan(serverId, memberId)); | |||||
| public static Task Unban(string serverId, string memberId) | |||||
| => Http.Delete(Endpoints.ServerBan(serverId, memberId)); | |||||
| //Invites | //Invites | ||||
| public static Task<APIResponses.CreateInvite> CreateInvite(string channelId, int maxAge, int maxUses, bool isTemporary, bool hasXkcdPass, HttpOptions options) | |||||
| public static Task<APIResponses.CreateInvite> CreateInvite(string channelId, int maxAge, int maxUses, bool isTemporary, bool hasXkcdPass) | |||||
| { | { | ||||
| var request = new APIRequests.CreateInvite { MaxAge = maxAge, MaxUses = maxUses, IsTemporary = isTemporary, HasXkcdPass = hasXkcdPass }; | var request = new APIRequests.CreateInvite { MaxAge = maxAge, MaxUses = maxUses, IsTemporary = isTemporary, HasXkcdPass = hasXkcdPass }; | ||||
| return Http.Post<APIResponses.CreateInvite>(Endpoints.ChannelInvites(channelId), request, options); | |||||
| return Http.Post<APIResponses.CreateInvite>(Endpoints.ChannelInvites(channelId), request); | |||||
| } | } | ||||
| public static Task<APIResponses.GetInvite> GetInvite(string id, HttpOptions options) | |||||
| => Http.Get<APIResponses.GetInvite>(Endpoints.Invite(id), options); | |||||
| public static Task AcceptInvite(string id, HttpOptions options) | |||||
| => Http.Post<APIResponses.AcceptInvite>(Endpoints.Invite(id), options); | |||||
| public static Task DeleteInvite(string id, HttpOptions options) | |||||
| => Http.Delete(Endpoints.Invite(id), options); | |||||
| public static Task<APIResponses.GetInvite> GetInvite(string id) | |||||
| => Http.Get<APIResponses.GetInvite>(Endpoints.Invite(id)); | |||||
| public static Task AcceptInvite(string id) | |||||
| => Http.Post<APIResponses.AcceptInvite>(Endpoints.Invite(id)); | |||||
| public static Task DeleteInvite(string id) | |||||
| => Http.Delete(Endpoints.Invite(id)); | |||||
| //Chat | //Chat | ||||
| public static Task<APIResponses.SendMessage> SendMessage(string channelId, string message, string[] mentions, HttpOptions options) | |||||
| public static Task<APIResponses.SendMessage> SendMessage(string channelId, string message, string[] mentions) | |||||
| { | { | ||||
| var request = new APIRequests.SendMessage { Content = message, Mentions = mentions }; | var request = new APIRequests.SendMessage { Content = message, Mentions = mentions }; | ||||
| return Http.Post<APIResponses.SendMessage>(Endpoints.ChannelMessages(channelId), request, options); | |||||
| return Http.Post<APIResponses.SendMessage>(Endpoints.ChannelMessages(channelId), request); | |||||
| } | } | ||||
| public static Task<APIResponses.EditMessage> EditMessage(string channelId, string messageId, string message, string[] mentions, HttpOptions options) | |||||
| public static Task<APIResponses.EditMessage> EditMessage(string channelId, string messageId, string message, string[] mentions) | |||||
| { | { | ||||
| var request = new APIRequests.EditMessage { Content = message, Mentions = mentions }; | var request = new APIRequests.EditMessage { Content = message, Mentions = mentions }; | ||||
| return Http.Patch<APIResponses.EditMessage>(Endpoints.ChannelMessage(channelId, messageId), request, options); | |||||
| return Http.Patch<APIResponses.EditMessage>(Endpoints.ChannelMessage(channelId, messageId), request); | |||||
| } | } | ||||
| public static Task SendIsTyping(string channelId, HttpOptions options) | |||||
| => Http.Post(Endpoints.ChannelTyping(channelId), options); | |||||
| public static Task DeleteMessage(string channelId, string msgId, HttpOptions options) | |||||
| => Http.Delete(Endpoints.ChannelMessage(channelId, msgId), options); | |||||
| public static Task SendIsTyping(string channelId) | |||||
| => Http.Post(Endpoints.ChannelTyping(channelId)); | |||||
| public static Task DeleteMessage(string channelId, string msgId) | |||||
| => Http.Delete(Endpoints.ChannelMessage(channelId, msgId)); | |||||
| //Voice | //Voice | ||||
| public static Task<APIResponses.GetRegions[]> GetVoiceRegions(HttpOptions options) | |||||
| => Http.Get<APIResponses.GetRegions[]>(Endpoints.VoiceRegions, options); | |||||
| public static Task<APIResponses.GetIce> GetVoiceIce(HttpOptions options) | |||||
| => Http.Get<APIResponses.GetIce>(Endpoints.VoiceIce, options); | |||||
| public static Task Mute(string serverId, string memberId, HttpOptions options) | |||||
| public static Task<APIResponses.GetRegions[]> GetVoiceRegions() | |||||
| => Http.Get<APIResponses.GetRegions[]>(Endpoints.VoiceRegions); | |||||
| public static Task<APIResponses.GetIce> GetVoiceIce() | |||||
| => Http.Get<APIResponses.GetIce>(Endpoints.VoiceIce); | |||||
| public static Task Mute(string serverId, string memberId) | |||||
| { | { | ||||
| var request = new APIRequests.SetMemberMute { Mute = true }; | var request = new APIRequests.SetMemberMute { Mute = true }; | ||||
| return Http.Patch(Endpoints.ServerMember(serverId, memberId), options); | |||||
| return Http.Patch(Endpoints.ServerMember(serverId, memberId)); | |||||
| } | } | ||||
| public static Task Unmute(string serverId, string memberId, HttpOptions options) | |||||
| public static Task Unmute(string serverId, string memberId) | |||||
| { | { | ||||
| var request = new APIRequests.SetMemberMute { Mute = false }; | var request = new APIRequests.SetMemberMute { Mute = false }; | ||||
| return Http.Patch(Endpoints.ServerMember(serverId, memberId), options); | |||||
| return Http.Patch(Endpoints.ServerMember(serverId, memberId)); | |||||
| } | } | ||||
| public static Task Deafen(string serverId, string memberId, HttpOptions options) | |||||
| public static Task Deafen(string serverId, string memberId) | |||||
| { | { | ||||
| var request = new APIRequests.SetMemberDeaf { Deaf = true }; | var request = new APIRequests.SetMemberDeaf { Deaf = true }; | ||||
| return Http.Patch(Endpoints.ServerMember(serverId, memberId), options); | |||||
| return Http.Patch(Endpoints.ServerMember(serverId, memberId)); | |||||
| } | } | ||||
| public static Task Undeafen(string serverId, string memberId, HttpOptions options) | |||||
| public static Task Undeafen(string serverId, string memberId) | |||||
| { | { | ||||
| var request = new APIRequests.SetMemberDeaf { Deaf = false }; | var request = new APIRequests.SetMemberDeaf { Deaf = false }; | ||||
| return Http.Patch(Endpoints.ServerMember(serverId, memberId), options); | |||||
| return Http.Patch(Endpoints.ServerMember(serverId, memberId)); | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| @@ -17,7 +17,6 @@ namespace Discord | |||||
| public const int FailedReconnectDelay = 10000; //Time in milliseconds to wait after a failed reconnect attempt | public const int FailedReconnectDelay = 10000; //Time in milliseconds to wait after a failed reconnect attempt | ||||
| private DiscordWebSocket _webSocket; | private DiscordWebSocket _webSocket; | ||||
| private HttpOptions _httpOptions; | |||||
| private bool _isReady; | private bool _isReady; | ||||
| public string UserId { get; private set; } | public string UserId { get; private set; } | ||||
| @@ -45,8 +44,6 @@ namespace Discord | |||||
| public DiscordClient() | public DiscordClient() | ||||
| { | { | ||||
| _isStopping = new ManualResetEventSlim(false); | _isStopping = new ManualResetEventSlim(false); | ||||
| string version = typeof(DiscordClient).GetTypeInfo().Assembly.GetName().Version.ToString(2); | |||||
| _httpOptions = new HttpOptions($"Discord.Net/{version} (https://github.com/RogueException/Discord.Net)"); | |||||
| _servers = new AsyncCache<Server, API.Models.ServerReference>( | _servers = new AsyncCache<Server, API.Models.ServerReference>( | ||||
| (key, parentKey) => new Server(key, this), | (key, parentKey) => new Server(key, this), | ||||
| @@ -160,7 +157,7 @@ namespace Discord | |||||
| try | try | ||||
| { | { | ||||
| await Task.Delay(ReconnectDelay); | await Task.Delay(ReconnectDelay); | ||||
| await _webSocket.ConnectAsync(Endpoints.WebSocket_Hub, true, _httpOptions); | |||||
| await _webSocket.ConnectAsync(Endpoints.WebSocket_Hub, true); | |||||
| break; | break; | ||||
| } | } | ||||
| catch (Exception) | catch (Exception) | ||||
| @@ -413,15 +410,26 @@ namespace Discord | |||||
| ) | ) | ||||
| .FirstOrDefault(); | .FirstOrDefault(); | ||||
| } | } | ||||
| public User FindChannelUser(Channel channel, string name) | |||||
| => FindChannelUser(channel.Id, name); | |||||
| public User FindChannelUser(string channelId, string name) | |||||
| public Membership FindMember(string serverId, string name) | |||||
| => FindMember(GetServer(serverId), name); | |||||
| public Membership FindMember(Server server, string name) | |||||
| { | { | ||||
| if (server == null) | |||||
| return null; | |||||
| if (name.StartsWith("<@") && name.EndsWith(">")) | |||||
| { | |||||
| var user = GetUser(name.Substring(2, name.Length - 3)); | |||||
| if (user == null) | |||||
| return null; | |||||
| return server.GetMembership(user.Id); | |||||
| } | |||||
| if (name.StartsWith("@")) | if (name.StartsWith("@")) | ||||
| name = name.Substring(1); | name = name.Substring(1); | ||||
| return _users | |||||
| .Where(x => string.Equals(x.Name, name, StringComparison.OrdinalIgnoreCase)) | |||||
| return server.Members | |||||
| .Where(x => string.Equals(x.User.Name, name, StringComparison.OrdinalIgnoreCase)) | |||||
| .FirstOrDefault(); | .FirstOrDefault(); | ||||
| } | } | ||||
| @@ -436,9 +444,11 @@ namespace Discord | |||||
| public Channel GetChannel(string id) => _channels[id]; | public Channel GetChannel(string id) => _channels[id]; | ||||
| public Channel FindChannel(string name) | public Channel FindChannel(string name) | ||||
| { | { | ||||
| if (name.StartsWith("<#") && name.EndsWith(">")) | |||||
| return GetChannel(name.Substring(2, name.Length - 3)); | |||||
| if (name.StartsWith("#")) | if (name.StartsWith("#")) | ||||
| name = name.Substring(1); | name = name.Substring(1); | ||||
| return _channels | return _channels | ||||
| .Where(x => string.Equals(x.Name, name, StringComparison.OrdinalIgnoreCase)) | .Where(x => string.Equals(x.Name, name, StringComparison.OrdinalIgnoreCase)) | ||||
| .FirstOrDefault(); | .FirstOrDefault(); | ||||
| @@ -447,9 +457,11 @@ namespace Discord | |||||
| => FindChannel(server.Id, name); | => FindChannel(server.Id, name); | ||||
| public Channel FindChannel(string serverId, string name) | public Channel FindChannel(string serverId, string name) | ||||
| { | { | ||||
| if (name.StartsWith("<#") && name.EndsWith(">")) | |||||
| return GetChannel(name.Substring(2, name.Length - 3)); | |||||
| if (name.StartsWith("#")) | if (name.StartsWith("#")) | ||||
| name = name.Substring(1); | name = name.Substring(1); | ||||
| return _channels | return _channels | ||||
| .Where(x => | .Where(x => | ||||
| string.Equals(x.Name, name, StringComparison.OrdinalIgnoreCase) && | string.Equals(x.Name, name, StringComparison.OrdinalIgnoreCase) && | ||||
| @@ -478,7 +490,7 @@ namespace Discord | |||||
| { | { | ||||
| try | try | ||||
| { | { | ||||
| var msgs = await DiscordAPI.GetMessages(channel.Id, count, _httpOptions); | |||||
| var msgs = await DiscordAPI.GetMessages(channel.Id, count); | |||||
| return msgs.OrderBy(x => x.Timestamp) | return msgs.OrderBy(x => x.Timestamp) | ||||
| .Select(x => | .Select(x => | ||||
| { | { | ||||
| @@ -501,13 +513,13 @@ namespace Discord | |||||
| _isStopping.Reset(); | _isStopping.Reset(); | ||||
| //Open websocket while we wait for login response | //Open websocket while we wait for login response | ||||
| Task socketTask = _webSocket.ConnectAsync(Endpoints.WebSocket_Hub, false, _httpOptions); | |||||
| var response = await DiscordAPI.Login(email, password, _httpOptions); | |||||
| _httpOptions.Token = response.Token; | |||||
| Task socketTask = _webSocket.ConnectAsync(Endpoints.WebSocket_Hub, false); | |||||
| var response = await DiscordAPI.Login(email, password); | |||||
| Http.Token = response.Token; | |||||
| //Wait for websocket to finish connecting, then send token | //Wait for websocket to finish connecting, then send token | ||||
| await socketTask; | await socketTask; | ||||
| _webSocket.Login(_httpOptions); | |||||
| _webSocket.Login(); | |||||
| _isReady = true; | _isReady = true; | ||||
| } | } | ||||
| @@ -516,13 +528,13 @@ namespace Discord | |||||
| _isStopping.Reset(); | _isStopping.Reset(); | ||||
| //Open websocket while we wait for login response | //Open websocket while we wait for login response | ||||
| Task socketTask = _webSocket.ConnectAsync(Endpoints.WebSocket_Hub, false, _httpOptions); | |||||
| var response = await DiscordAPI.LoginAnonymous(username, _httpOptions); | |||||
| _httpOptions.Token = response.Token; | |||||
| Task socketTask = _webSocket.ConnectAsync(Endpoints.WebSocket_Hub, false); | |||||
| var response = await DiscordAPI.LoginAnonymous(username); | |||||
| Http.Token = response.Token; | |||||
| //Wait for websocket to finish connecting, then send token | //Wait for websocket to finish connecting, then send token | ||||
| await socketTask; | await socketTask; | ||||
| _webSocket.Login(_httpOptions); | |||||
| _webSocket.Login(); | |||||
| _isReady = true; | _isReady = true; | ||||
| } | } | ||||
| @@ -543,7 +555,7 @@ namespace Discord | |||||
| public async Task<Server> CreateServer(string name, string region) | public async Task<Server> CreateServer(string name, string region) | ||||
| { | { | ||||
| CheckReady(); | CheckReady(); | ||||
| var response = await DiscordAPI.CreateServer(name, region, _httpOptions); | |||||
| var response = await DiscordAPI.CreateServer(name, region); | |||||
| return _servers.Update(response.Id, response); | return _servers.Update(response.Id, response); | ||||
| } | } | ||||
| public Task<Server> LeaveServer(Server server) | public Task<Server> LeaveServer(Server server) | ||||
| @@ -553,7 +565,7 @@ namespace Discord | |||||
| CheckReady(); | CheckReady(); | ||||
| try | try | ||||
| { | { | ||||
| await DiscordAPI.LeaveServer(serverId, _httpOptions); | |||||
| await DiscordAPI.LeaveServer(serverId); | |||||
| } | } | ||||
| catch (WebException ex) when ((ex.Response as HttpWebResponse)?.StatusCode == HttpStatusCode.NotFound) {} | catch (WebException ex) when ((ex.Response as HttpWebResponse)?.StatusCode == HttpStatusCode.NotFound) {} | ||||
| return _servers.Remove(serverId); | return _servers.Remove(serverId); | ||||
| @@ -565,7 +577,7 @@ namespace Discord | |||||
| public async Task<Channel> CreateChannel(string serverId, string name, string type) | public async Task<Channel> CreateChannel(string serverId, string name, string type) | ||||
| { | { | ||||
| CheckReady(); | CheckReady(); | ||||
| var response = await DiscordAPI.CreateChannel(serverId, name, type, _httpOptions); | |||||
| var response = await DiscordAPI.CreateChannel(serverId, name, type); | |||||
| return _channels.Update(response.Id, serverId, response); | return _channels.Update(response.Id, serverId, response); | ||||
| } | } | ||||
| public Task<Channel> CreatePMChannel(User user) | public Task<Channel> CreatePMChannel(User user) | ||||
| @@ -573,7 +585,7 @@ namespace Discord | |||||
| public async Task<Channel> CreatePMChannel(string recipientId) | public async Task<Channel> CreatePMChannel(string recipientId) | ||||
| { | { | ||||
| CheckReady(); | CheckReady(); | ||||
| var response = await DiscordAPI.CreatePMChannel(UserId, recipientId, _httpOptions); | |||||
| var response = await DiscordAPI.CreatePMChannel(UserId, recipientId); | |||||
| return _channels.Update(response.Id, response); | return _channels.Update(response.Id, response); | ||||
| } | } | ||||
| public Task<Channel> DestroyChannel(Channel channel) | public Task<Channel> DestroyChannel(Channel channel) | ||||
| @@ -583,7 +595,7 @@ namespace Discord | |||||
| CheckReady(); | CheckReady(); | ||||
| try | try | ||||
| { | { | ||||
| var response = await DiscordAPI.DestroyChannel(channelId, _httpOptions); | |||||
| var response = await DiscordAPI.DestroyChannel(channelId); | |||||
| } | } | ||||
| catch (WebException ex) when ((ex.Response as HttpWebResponse)?.StatusCode == HttpStatusCode.NotFound) { } | catch (WebException ex) when ((ex.Response as HttpWebResponse)?.StatusCode == HttpStatusCode.NotFound) { } | ||||
| return _channels.Remove(channelId); | return _channels.Remove(channelId); | ||||
| @@ -599,7 +611,7 @@ namespace Discord | |||||
| public Task Ban(string serverId, string userId) | public Task Ban(string serverId, string userId) | ||||
| { | { | ||||
| CheckReady(); | CheckReady(); | ||||
| return DiscordAPI.Ban(serverId, userId, _httpOptions); | |||||
| return DiscordAPI.Ban(serverId, userId); | |||||
| } | } | ||||
| public Task Unban(Server server, User user) | public Task Unban(Server server, User user) | ||||
| => Unban(server.Id, user.Id); | => Unban(server.Id, user.Id); | ||||
| @@ -612,7 +624,7 @@ namespace Discord | |||||
| CheckReady(); | CheckReady(); | ||||
| try | try | ||||
| { | { | ||||
| await DiscordAPI.Unban(serverId, userId, _httpOptions); | |||||
| await DiscordAPI.Unban(serverId, userId); | |||||
| } | } | ||||
| catch (WebException ex) when ((ex.Response as HttpWebResponse)?.StatusCode == HttpStatusCode.NotFound) { } | catch (WebException ex) when ((ex.Response as HttpWebResponse)?.StatusCode == HttpStatusCode.NotFound) { } | ||||
| } | } | ||||
| @@ -629,7 +641,7 @@ namespace Discord | |||||
| public async Task<Invite> CreateInvite(string channelId, int maxAge, int maxUses, bool isTemporary, bool hasXkcdPass) | public async Task<Invite> CreateInvite(string channelId, int maxAge, int maxUses, bool isTemporary, bool hasXkcdPass) | ||||
| { | { | ||||
| CheckReady(); | CheckReady(); | ||||
| var response = await DiscordAPI.CreateInvite(channelId, maxAge, maxUses, isTemporary, hasXkcdPass, _httpOptions); | |||||
| var response = await DiscordAPI.CreateInvite(channelId, maxAge, maxUses, isTemporary, hasXkcdPass); | |||||
| _channels.Update(response.Channel.Id, response.Server.Id, response.Channel); | _channels.Update(response.Channel.Id, response.Server.Id, response.Channel); | ||||
| _servers.Update(response.Server.Id, response.Server); | _servers.Update(response.Server.Id, response.Server); | ||||
| _users.Update(response.Inviter.Id, response.Inviter); | _users.Update(response.Inviter.Id, response.Inviter); | ||||
| @@ -648,7 +660,7 @@ namespace Discord | |||||
| public async Task<Invite> GetInvite(string id) | public async Task<Invite> GetInvite(string id) | ||||
| { | { | ||||
| CheckReady(); | CheckReady(); | ||||
| var response = await DiscordAPI.GetInvite(id, _httpOptions); | |||||
| var response = await DiscordAPI.GetInvite(id); | |||||
| return new Invite(response.Code, response.XkcdPass, this) | return new Invite(response.Code, response.XkcdPass, this) | ||||
| { | { | ||||
| ChannelId = response.Channel.Id, | ChannelId = response.Channel.Id, | ||||
| @@ -659,14 +671,14 @@ namespace Discord | |||||
| public Task AcceptInvite(Invite invite) | public Task AcceptInvite(Invite invite) | ||||
| { | { | ||||
| CheckReady(); | CheckReady(); | ||||
| return DiscordAPI.AcceptInvite(invite.Code, _httpOptions); | |||||
| return DiscordAPI.AcceptInvite(invite.Code); | |||||
| } | } | ||||
| public async Task AcceptInvite(string id) | public async Task AcceptInvite(string id) | ||||
| { | { | ||||
| CheckReady(); | CheckReady(); | ||||
| //Check if this is a human-readable link and get its ID | //Check if this is a human-readable link and get its ID | ||||
| var response = await DiscordAPI.GetInvite(id, _httpOptions); | |||||
| await DiscordAPI.AcceptInvite(response.Code, _httpOptions); | |||||
| var response = await DiscordAPI.GetInvite(id); | |||||
| await DiscordAPI.AcceptInvite(response.Code); | |||||
| } | } | ||||
| public async Task DeleteInvite(string id) | public async Task DeleteInvite(string id) | ||||
| { | { | ||||
| @@ -674,8 +686,8 @@ namespace Discord | |||||
| try | try | ||||
| { | { | ||||
| //Check if this is a human-readable link and get its ID | //Check if this is a human-readable link and get its ID | ||||
| var response = await DiscordAPI.GetInvite(id, _httpOptions); | |||||
| await DiscordAPI.DeleteInvite(response.Code, _httpOptions); | |||||
| var response = await DiscordAPI.GetInvite(id); | |||||
| await DiscordAPI.DeleteInvite(response.Code); | |||||
| } | } | ||||
| catch (WebException ex) when ((ex.Response as HttpWebResponse)?.StatusCode == HttpStatusCode.NotFound) { } | catch (WebException ex) when ((ex.Response as HttpWebResponse)?.StatusCode == HttpStatusCode.NotFound) { } | ||||
| } | } | ||||
| @@ -693,7 +705,7 @@ namespace Discord | |||||
| if (text.Length <= 2000) | if (text.Length <= 2000) | ||||
| { | { | ||||
| var msg = await DiscordAPI.SendMessage(channelId, text, mentions, _httpOptions); | |||||
| var msg = await DiscordAPI.SendMessage(channelId, text, mentions); | |||||
| return new Message[] { _messages.Update(msg.Id, channelId, msg) }; | return new Message[] { _messages.Update(msg.Id, channelId, msg) }; | ||||
| } | } | ||||
| else | else | ||||
| @@ -703,7 +715,7 @@ namespace Discord | |||||
| for (int i = 0; i < blockCount; i++) | for (int i = 0; i < blockCount; i++) | ||||
| { | { | ||||
| int index = i * DiscordAPI.MaxMessageSize; | int index = i * DiscordAPI.MaxMessageSize; | ||||
| var msg = await DiscordAPI.SendMessage(channelId, text.Substring(index, Math.Min(2000, text.Length - index)), mentions, _httpOptions); | |||||
| var msg = await DiscordAPI.SendMessage(channelId, text.Substring(index, Math.Min(2000, text.Length - index)), mentions); | |||||
| result[i] = _messages.Update(msg.Id, channelId, msg); | result[i] = _messages.Update(msg.Id, channelId, msg); | ||||
| await Task.Delay(1000); | await Task.Delay(1000); | ||||
| } | } | ||||
| @@ -727,7 +739,7 @@ namespace Discord | |||||
| if (text.Length > DiscordAPI.MaxMessageSize) | if (text.Length > DiscordAPI.MaxMessageSize) | ||||
| text = text.Substring(0, DiscordAPI.MaxMessageSize); | text = text.Substring(0, DiscordAPI.MaxMessageSize); | ||||
| var msg = await DiscordAPI.EditMessage(channelId, messageId, text, mentions, _httpOptions); | |||||
| var msg = await DiscordAPI.EditMessage(channelId, messageId, text, mentions); | |||||
| _messages.Update(msg.Id, channelId, msg); | _messages.Update(msg.Id, channelId, msg); | ||||
| } | } | ||||
| @@ -737,7 +749,7 @@ namespace Discord | |||||
| { | { | ||||
| try | try | ||||
| { | { | ||||
| await DiscordAPI.DeleteMessage(channelId, msgId, _httpOptions); | |||||
| await DiscordAPI.DeleteMessage(channelId, msgId); | |||||
| return _messages.Remove(msgId); | return _messages.Remove(msgId); | ||||
| } | } | ||||
| catch (WebException ex) when ((ex.Response as HttpWebResponse)?.StatusCode == HttpStatusCode.NotFound) { } | catch (WebException ex) when ((ex.Response as HttpWebResponse)?.StatusCode == HttpStatusCode.NotFound) { } | ||||
| @@ -755,7 +767,7 @@ namespace Discord | |||||
| public Task Mute(string serverId, string userId) | public Task Mute(string serverId, string userId) | ||||
| { | { | ||||
| CheckReady(); | CheckReady(); | ||||
| return DiscordAPI.Mute(serverId, userId, _httpOptions); | |||||
| return DiscordAPI.Mute(serverId, userId); | |||||
| } | } | ||||
| public Task Unmute(Server server, User user) | public Task Unmute(Server server, User user) | ||||
| @@ -767,7 +779,7 @@ namespace Discord | |||||
| public Task Unmute(string serverId, string userId) | public Task Unmute(string serverId, string userId) | ||||
| { | { | ||||
| CheckReady(); | CheckReady(); | ||||
| return DiscordAPI.Unmute(serverId, userId, _httpOptions); | |||||
| return DiscordAPI.Unmute(serverId, userId); | |||||
| } | } | ||||
| public Task Deafen(Server server, User user) | public Task Deafen(Server server, User user) | ||||
| @@ -779,7 +791,7 @@ namespace Discord | |||||
| public Task Deafen(string serverId, string userId) | public Task Deafen(string serverId, string userId) | ||||
| { | { | ||||
| CheckReady(); | CheckReady(); | ||||
| return DiscordAPI.Deafen(serverId, userId, _httpOptions); | |||||
| return DiscordAPI.Deafen(serverId, userId); | |||||
| } | } | ||||
| public Task Undeafen(Server server, User user) | public Task Undeafen(Server server, User user) | ||||
| @@ -791,7 +803,7 @@ namespace Discord | |||||
| public Task Undeafen(string serverId, string userId) | public Task Undeafen(string serverId, string userId) | ||||
| { | { | ||||
| CheckReady(); | CheckReady(); | ||||
| return DiscordAPI.Undeafen(serverId, userId, _httpOptions); | |||||
| return DiscordAPI.Undeafen(serverId, userId); | |||||
| } | } | ||||
| private void CheckReady() | private void CheckReady() | ||||
| @@ -25,7 +25,7 @@ namespace Discord | |||||
| private DateTime _lastHeartbeat; | private DateTime _lastHeartbeat; | ||||
| private AutoResetEvent _connectWaitOnLogin, _connectWaitOnLogin2; | private AutoResetEvent _connectWaitOnLogin, _connectWaitOnLogin2; | ||||
| public async Task ConnectAsync(string url, bool autoLogin, HttpOptions options) | |||||
| public async Task ConnectAsync(string url, bool autoLogin) | |||||
| { | { | ||||
| await DisconnectAsync(); | await DisconnectAsync(); | ||||
| @@ -58,12 +58,12 @@ namespace Discord | |||||
| }); | }); | ||||
| if (autoLogin) | if (autoLogin) | ||||
| Login(options); | |||||
| Login(); | |||||
| } | } | ||||
| public void Login(HttpOptions options) | |||||
| public void Login() | |||||
| { | { | ||||
| WebSocketCommands.Login msg = new WebSocketCommands.Login(); | WebSocketCommands.Login msg = new WebSocketCommands.Login(); | ||||
| msg.Payload.Token = options.Token; | |||||
| msg.Payload.Token = Http.Token; | |||||
| msg.Payload.Properties["$os"] = ""; | msg.Payload.Properties["$os"] = ""; | ||||
| msg.Payload.Properties["$browser"] = ""; | msg.Payload.Properties["$browser"] = ""; | ||||
| msg.Payload.Properties["$device"] = "Discord.Net"; | msg.Payload.Properties["$device"] = "Discord.Net"; | ||||
| @@ -2,23 +2,13 @@ | |||||
| using System; | using System; | ||||
| using System.IO; | using System.IO; | ||||
| using System.IO.Compression; | using System.IO.Compression; | ||||
| using System.Net; | |||||
| using System.Text; | using System.Text; | ||||
| using System.Threading.Tasks; | using System.Threading.Tasks; | ||||
| using System.Net.Http; | |||||
| using System.Reflection; | |||||
| namespace Discord.Helpers | namespace Discord.Helpers | ||||
| { | { | ||||
| internal class HttpOptions | |||||
| { | |||||
| public readonly string UserAgent; | |||||
| public string Token; | |||||
| public HttpOptions(string userAgent) | |||||
| { | |||||
| UserAgent = userAgent; | |||||
| } | |||||
| } | |||||
| internal static class Http | internal static class Http | ||||
| { | { | ||||
| #if DEBUG | #if DEBUG | ||||
| @@ -26,151 +16,120 @@ namespace Discord.Helpers | |||||
| #else | #else | ||||
| private const bool _isDebug = false; | private const bool _isDebug = false; | ||||
| #endif | #endif | ||||
| internal static Task<ResponseT> Get<ResponseT>(string path, HttpOptions options) | |||||
| private static readonly HttpClient _client; | |||||
| private static readonly HttpMethod _patch = new HttpMethod("PATCH"); //Not sure why this isn't a default... | |||||
| static Http() | |||||
| { | |||||
| _client = new HttpClient(); | |||||
| _client.DefaultRequestHeaders.Add("Accept", "*/*"); | |||||
| _client.DefaultRequestHeaders.Add("Accept-language", "en-US;q=0.8"); | |||||
| string version = typeof(Http).GetTypeInfo().Assembly.GetName().Version.ToString(2); | |||||
| _client.DefaultRequestHeaders.Add("User-agent", $"Discord.Net/{version} (https://github.com/RogueException/Discord.Net)"); | |||||
| } | |||||
| private static string _token; | |||||
| public static string Token | |||||
| { | |||||
| get { return _token; } | |||||
| set | |||||
| { | |||||
| _token = value; | |||||
| _client.DefaultRequestHeaders.Add("Authorization", _token); | |||||
| } | |||||
| } | |||||
| internal static Task<ResponseT> Get<ResponseT>(string path) | |||||
| where ResponseT : class | where ResponseT : class | ||||
| => Send<ResponseT>("GET", path, null, options); | |||||
| internal static Task<string> Get(string path, HttpOptions options) | |||||
| => Send("GET", path, null, options); | |||||
| => Send<ResponseT>(HttpMethod.Get, path, null); | |||||
| internal static Task<string> Get(string path) | |||||
| => Send(HttpMethod.Get, path, null); | |||||
| internal static Task<ResponseT> Post<ResponseT>(string path, object data, HttpOptions options) | |||||
| internal static Task<ResponseT> Post<ResponseT>(string path, object data) | |||||
| where ResponseT : class | where ResponseT : class | ||||
| => Send<ResponseT>("POST", path, data, options); | |||||
| internal static Task<string> Post(string path, object data, HttpOptions options) | |||||
| => Send("POST", path, data, options); | |||||
| internal static Task<ResponseT> Post<ResponseT>(string path, HttpOptions options) | |||||
| => Send<ResponseT>(HttpMethod.Post, path, data); | |||||
| internal static Task<string> Post(string path, object data) | |||||
| => Send(HttpMethod.Post, path, data); | |||||
| internal static Task<ResponseT> Post<ResponseT>(string path) | |||||
| where ResponseT : class | where ResponseT : class | ||||
| => Send<ResponseT>("POST", path, null, options); | |||||
| internal static Task<string> Post(string path, HttpOptions options) | |||||
| => Send("POST", path, null, options); | |||||
| => Send<ResponseT>(HttpMethod.Post, path, null); | |||||
| internal static Task<string> Post(string path) | |||||
| => Send(HttpMethod.Post, path, null); | |||||
| internal static Task<ResponseT> Put<ResponseT>(string path, object data, HttpOptions options) | |||||
| internal static Task<ResponseT> Put<ResponseT>(string path, object data) | |||||
| where ResponseT : class | where ResponseT : class | ||||
| => Send<ResponseT>("PUT", path, data, options); | |||||
| internal static Task<string> Put(string path, object data, HttpOptions options) | |||||
| => Send("PUT", path, data, options); | |||||
| internal static Task<ResponseT> Put<ResponseT>(string path, HttpOptions options) | |||||
| => Send<ResponseT>(HttpMethod.Put, path, data); | |||||
| internal static Task<string> Put(string path, object data) | |||||
| => Send(HttpMethod.Put, path, data); | |||||
| internal static Task<ResponseT> Put<ResponseT>(string path) | |||||
| where ResponseT : class | where ResponseT : class | ||||
| => Send<ResponseT>("PUT", path, null, options); | |||||
| internal static Task<string> Put(string path, HttpOptions options) | |||||
| => Send("PUT", path, null, options); | |||||
| => Send<ResponseT>(HttpMethod.Put, path, null); | |||||
| internal static Task<string> Put(string path) | |||||
| => Send(HttpMethod.Put, path, null); | |||||
| internal static Task<ResponseT> Patch<ResponseT>(string path, object data, HttpOptions options) | |||||
| internal static Task<ResponseT> Patch<ResponseT>(string path, object data) | |||||
| where ResponseT : class | where ResponseT : class | ||||
| => Send<ResponseT>("PATCH", path, data, options); | |||||
| internal static Task<string> Patch(string path, object data, HttpOptions options) | |||||
| => Send("PATCH", path, data, options); | |||||
| internal static Task<ResponseT> Patch<ResponseT>(string path, HttpOptions options) | |||||
| => Send<ResponseT>(_patch, path, data); | |||||
| internal static Task<string> Patch(string path, object data) | |||||
| => Send(_patch, path, data); | |||||
| internal static Task<ResponseT> Patch<ResponseT>(string path) | |||||
| where ResponseT : class | where ResponseT : class | ||||
| => Send<ResponseT>("PATCH", path, null, options); | |||||
| internal static Task<string> Patch(string path, HttpOptions options) | |||||
| => Send("PATCH", path, null, options); | |||||
| => Send<ResponseT>(_patch, path, null); | |||||
| internal static Task<string> Patch(string path) | |||||
| => Send(_patch, path, null); | |||||
| internal static Task<ResponseT> Delete<ResponseT>(string path, object data, HttpOptions options) | |||||
| internal static Task<ResponseT> Delete<ResponseT>(string path, object data) | |||||
| where ResponseT : class | where ResponseT : class | ||||
| => Send<ResponseT>("DELETE", path, data, options); | |||||
| internal static Task<string> Delete(string path, object data, HttpOptions options) | |||||
| => Send("DELETE", path, data, options); | |||||
| internal static Task<ResponseT> Delete<ResponseT>(string path, HttpOptions options) | |||||
| => Send<ResponseT>(HttpMethod.Delete, path, data); | |||||
| internal static Task<string> Delete(string path, object data) | |||||
| => Send(HttpMethod.Delete, path, data); | |||||
| internal static Task<ResponseT> Delete<ResponseT>(string path) | |||||
| where ResponseT : class | where ResponseT : class | ||||
| => Send<ResponseT>("DELETE", path, null, options); | |||||
| internal static Task<string> Delete(string path, HttpOptions options) | |||||
| => Send("DELETE", path, null, options); | |||||
| => Send<ResponseT>(HttpMethod.Delete, path, null); | |||||
| internal static Task<string> Delete(string path) | |||||
| => Send(HttpMethod.Delete, path, null); | |||||
| internal static async Task<ResponseT> Send<ResponseT>(string method, string path, object data, HttpOptions options) | |||||
| internal static async Task<ResponseT> Send<ResponseT>(HttpMethod method, string path, object data) | |||||
| where ResponseT : class | where ResponseT : class | ||||
| { | { | ||||
| string requestJson = data != null ? JsonConvert.SerializeObject(data) : null; | string requestJson = data != null ? JsonConvert.SerializeObject(data) : null; | ||||
| string responseJson = await SendRequest(method, path, requestJson, options, true); | |||||
| string responseJson = await SendRequest(method, path, requestJson, true); | |||||
| var response = JsonConvert.DeserializeObject<ResponseT>(responseJson); | var response = JsonConvert.DeserializeObject<ResponseT>(responseJson); | ||||
| #if DEBUG | #if DEBUG | ||||
| CheckResponse(responseJson, response); | CheckResponse(responseJson, response); | ||||
| #endif | #endif | ||||
| return response; | return response; | ||||
| } | } | ||||
| internal static async Task<string> Send(string method, string path, object data, HttpOptions options) | |||||
| internal static async Task<string> Send(HttpMethod method, string path, object data) | |||||
| { | { | ||||
| string requestJson = data != null ? JsonConvert.SerializeObject(data) : null; | string requestJson = data != null ? JsonConvert.SerializeObject(data) : null; | ||||
| string responseJson = await SendRequest(method, path, requestJson, options, _isDebug); | |||||
| string responseJson = await SendRequest(method, path, requestJson, _isDebug); | |||||
| #if DEBUG | #if DEBUG | ||||
| CheckEmptyResponse(responseJson); | CheckEmptyResponse(responseJson); | ||||
| #endif | #endif | ||||
| return responseJson; | return responseJson; | ||||
| } | } | ||||
| private static async Task<string> SendRequest(string method, string path, string data, HttpOptions options, bool hasResponse) | |||||
| private static async Task<string> SendRequest(HttpMethod method, string path, string data, bool hasResponse) | |||||
| { | { | ||||
| //Create Request | //Create Request | ||||
| HttpWebRequest request = WebRequest.CreateHttp(path); | |||||
| request.Accept = "*/*"; | |||||
| request.Method = method; | |||||
| request.Proxy = null; | |||||
| request.Headers[HttpRequestHeader.AcceptLanguage] = "en-US;q=0.8"; | |||||
| request.Headers[HttpRequestHeader.AcceptEncoding] = "gzip, deflate"; | |||||
| request.Headers[HttpRequestHeader.UserAgent] = options.UserAgent; | |||||
| request.Headers[HttpRequestHeader.Authorization] = options.Token; | |||||
| //request.UserAgent = options.UserAgent; | |||||
| HttpRequestMessage msg = new HttpRequestMessage(method, path); | |||||
| //Add Payload | //Add Payload | ||||
| if (data != null) | if (data != null) | ||||
| { | |||||
| byte[] buffer = Encoding.UTF8.GetBytes(data); | |||||
| using (var payload = await request.GetRequestStreamAsync()) | |||||
| payload.Write(buffer, 0, buffer.Length); | |||||
| request.ContentType = "application/json"; | |||||
| } | |||||
| msg.Content = new StringContent(data, Encoding.UTF8, "application/json"); | |||||
| //Get Response | |||||
| using (var response = (HttpWebResponse)(await request.GetResponseAsync())) | |||||
| if (!hasResponse) | |||||
| { | { | ||||
| if (hasResponse) | |||||
| { | |||||
| using (var stream = response.GetResponseStream()) | |||||
| using (var reader = new BinaryReader(stream)) | |||||
| using (var largeBuffer = new MemoryStream()) | |||||
| { | |||||
| //Read the response in small chunks and add them to a larger buffer. | |||||
| //ContentLength isn't always provided, so this is safer. | |||||
| int bytesRead = 0; | |||||
| byte[] smallBuffer = new byte[4096]; | |||||
| while ((bytesRead = reader.Read(smallBuffer, 0, smallBuffer.Length)) > 0) | |||||
| largeBuffer.Write(smallBuffer, 0, bytesRead); | |||||
| //Do we need to decompress? | |||||
| string encoding = response.Headers[HttpResponseHeader.ContentEncoding]; | |||||
| if (!string.IsNullOrEmpty(encoding)) | |||||
| { | |||||
| largeBuffer.Position = 0; | |||||
| using (var decoder = GetDecoder(encoding, largeBuffer)) | |||||
| using (var decodedStream = new MemoryStream()) | |||||
| { | |||||
| decoder.CopyTo(decodedStream); | |||||
| #if !DOTNET | |||||
| return Encoding.UTF8.GetString(decodedStream.GetBuffer(), 0, (int)decodedStream.Length); | |||||
| #else | |||||
| ArraySegment<byte> buffer; | |||||
| if (!decodedStream.TryGetBuffer(out buffer)) | |||||
| throw new InvalidOperationException("Failed to get response buffer."); | |||||
| return Encoding.UTF8.GetString(buffer.Array, buffer.Offset, (int)decodedStream.Length); | |||||
| #endif | |||||
| } | |||||
| } | |||||
| else | |||||
| { | |||||
| #if !DOTNET | |||||
| return Encoding.UTF8.GetString(largeBuffer.GetBuffer(), 0, (int)largeBuffer.Length); | |||||
| #else | |||||
| ArraySegment<byte> buffer; | |||||
| if (!largeBuffer.TryGetBuffer(out buffer)) | |||||
| throw new InvalidOperationException("Failed to get response buffer."); | |||||
| return Encoding.UTF8.GetString(buffer.Array, buffer.Offset, (int)largeBuffer.Length); | |||||
| #endif | |||||
| } | |||||
| } | |||||
| } | |||||
| else | |||||
| return null; | |||||
| await _client.SendAsync(msg, HttpCompletionOption.ResponseHeadersRead); | |||||
| return null; | |||||
| } | } | ||||
| else | |||||
| { | |||||
| var response = await _client.SendAsync(msg, HttpCompletionOption.ResponseContentRead); | |||||
| return await response.Content.ReadAsStringAsync(); | |||||
| } | |||||
| } | } | ||||
| private static Stream GetDecoder(string contentEncoding, MemoryStream encodedStream) | private static Stream GetDecoder(string contentEncoding, MemoryStream encodedStream) | ||||
| @@ -24,5 +24,8 @@ | |||||
| "Newtonsoft.Json": "7.0.1" | "Newtonsoft.Json": "7.0.1" | ||||
| } | } | ||||
| } | } | ||||
| }, | |||||
| "dependencies": { | |||||
| "Microsoft.Net.Http": "2.2.22" | |||||
| } | } | ||||
| } | } | ||||