Browse Source

Improved all things HTTP related

tags/docs-0.9
Brandon Smith 9 years ago
parent
commit
5fb96188c9
6 changed files with 192 additions and 218 deletions
  1. +1
    -0
      src/Discord.Net.Net45/Discord.Net.csproj
  2. +53
    -54
      src/Discord.Net/API/DiscordAPI.cs
  3. +54
    -42
      src/Discord.Net/DiscordClient.cs
  4. +4
    -4
      src/Discord.Net/DiscordWebSocket.cs
  5. +77
    -118
      src/Discord.Net/Helpers/Http.cs
  6. +3
    -0
      src/Discord.Net/project.json

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

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


+ 53
- 54
src/Discord.Net/API/DiscordAPI.cs View File

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

+ 54
- 42
src/Discord.Net/DiscordClient.cs View File

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


+ 4
- 4
src/Discord.Net/DiscordWebSocket.cs View File

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


+ 77
- 118
src/Discord.Net/Helpers/Http.cs View File

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


+ 3
- 0
src/Discord.Net/project.json View File

@@ -24,5 +24,8 @@
"Newtonsoft.Json": "7.0.1" "Newtonsoft.Json": "7.0.1"
} }
} }
},
"dependencies": {
"Microsoft.Net.Http": "2.2.22"
} }
} }

Loading…
Cancel
Save