Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 3 additions & 4 deletions TwitchLib.Api.Core.Interfaces/IHttpCallHandler.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
#nullable disable
using System.Collections.Generic;
using System.Collections.Generic;
using System.Threading.Tasks;
using TwitchLib.Api.Core.Enums;

Expand All @@ -10,7 +9,7 @@
/// </summary>
public interface IHttpCallHandler
{
Task<KeyValuePair<int, string>> GeneralRequestAsync(string url, string method, string payload = null, ApiVersion api = ApiVersion.Helix, string clientId = null, string accessToken = null);
Task<KeyValuePair<int, string>> GeneralRequestAsync(string url, string method, string? payload = null, ApiVersion api = ApiVersion.Helix, string? clientId = null, string? accessToken = null);

Check warning on line 12 in TwitchLib.Api.Core.Interfaces/IHttpCallHandler.cs

View workflow job for this annotation

GitHub Actions / check-buildstatus

Missing XML comment for publicly visible type or member 'IHttpCallHandler.GeneralRequestAsync(string, string, string?, ApiVersion, string?, string?)'
Task PutBytesAsync(string url, byte[] payload);

Check warning on line 13 in TwitchLib.Api.Core.Interfaces/IHttpCallHandler.cs

View workflow job for this annotation

GitHub Actions / check-buildstatus

Missing XML comment for publicly visible type or member 'IHttpCallHandler.PutBytesAsync(string, byte[])'
Task<int> RequestReturnResponseCodeAsync(string url, string method, List<KeyValuePair<string, string>> getParams = null);
Task<int> RequestReturnResponseCodeAsync(string url, string method, List<KeyValuePair<string, string>>? getParams = null);

Check warning on line 14 in TwitchLib.Api.Core.Interfaces/IHttpCallHandler.cs

View workflow job for this annotation

GitHub Actions / check-buildstatus

Missing XML comment for publicly visible type or member 'IHttpCallHandler.RequestReturnResponseCodeAsync(string, string, List<KeyValuePair<string, string>>?)'
}
60 changes: 30 additions & 30 deletions TwitchLib.Api.Core/ApiBase.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
#nullable disable
using System;
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
Expand Down Expand Up @@ -34,7 +35,7 @@ public ApiBase(IApiSettings settings, IRateLimiter rateLimiter, IHttpCallHandler
_jsonSerializer = new TwitchLibJsonSerializer();
}

public async ValueTask<string> GetAccessTokenAsync(string accessToken = null)
public async ValueTask<string?> GetAccessTokenAsync(string? accessToken = null)
{
if (!string.IsNullOrWhiteSpace(accessToken))
return accessToken;
Expand All @@ -51,7 +52,7 @@ public async ValueTask<string> GetAccessTokenAsync(string accessToken = null)
return null;
}

internal async Task<string> GenerateServerBasedAccessToken()
internal async Task<string?> GenerateServerBasedAccessToken()
{
var result = await _http.GeneralRequestAsync($"{BaseAuth}/token?client_id={Settings.ClientId}&client_secret={Settings.Secret}&grant_type=client_credentials", "POST", null, ApiVersion.Auth, Settings.ClientId, null).ConfigureAwait(false);
if (result.Key == 200)
Expand All @@ -74,7 +75,7 @@ internal void ForceAccessTokenAndClientIdForHelix(string clientId, string access
throw new ClientIdAndOAuthTokenRequired("As of May 1, all calls to Twitch's Helix API require Client-ID and OAuth access token be set. Example: api.Settings.AccessToken = \"twitch-oauth-access-token-here\"; api.Settings.ClientId = \"twitch-client-id-here\";");
}

protected async Task<string> TwitchGetAsync(string resource, ApiVersion api, List<KeyValuePair<string, string>> getParams = null, string accessToken = null, string clientId = null, string customBase = null)
protected async Task<string> TwitchGetAsync(string resource, ApiVersion api, List<KeyValuePair<string, string>>? getParams = null, string? accessToken = null, string? clientId = null, string? customBase = null)
{
var url = ConstructResourceUrl(resource, getParams, api, customBase);

Expand All @@ -87,7 +88,7 @@ protected async Task<string> TwitchGetAsync(string resource, ApiVersion api, Lis
return await _rateLimiter.Perform(async () => (await _http.GeneralRequestAsync(url, "GET", null, api, clientId, accessToken).ConfigureAwait(false)).Value).ConfigureAwait(false);
}

protected async Task<T> TwitchGetGenericAsync<T>(string resource, ApiVersion api, List<KeyValuePair<string, string>> getParams = null, string accessToken = null, string clientId = null, string customBase = null)
protected async Task<T> TwitchGetGenericAsync<T>(string resource, ApiVersion api, List<KeyValuePair<string, string>>? getParams = null, string? accessToken = null, string? clientId = null, string? customBase = null)
{
var url = ConstructResourceUrl(resource, getParams, api, customBase);

Expand All @@ -100,7 +101,7 @@ protected async Task<T> TwitchGetGenericAsync<T>(string resource, ApiVersion api
return await _rateLimiter.Perform(async () => JsonConvert.DeserializeObject<T>((await _http.GeneralRequestAsync(url, "GET", null, api, clientId, accessToken).ConfigureAwait(false)).Value, _twitchLibJsonDeserializer)).ConfigureAwait(false);
}

protected async Task<T> TwitchPatchGenericAsync<T>(string resource, ApiVersion api, string payload, List<KeyValuePair<string, string>> getParams = null, string accessToken = null, string clientId = null, string customBase = null)
protected async Task<T> TwitchPatchGenericAsync<T>(string resource, ApiVersion api, string payload, List<KeyValuePair<string, string>>? getParams = null, string? accessToken = null, string? clientId = null, string? customBase = null)
{
var url = ConstructResourceUrl(resource, getParams, api, customBase);

Expand All @@ -113,7 +114,7 @@ protected async Task<T> TwitchPatchGenericAsync<T>(string resource, ApiVersion a
return await _rateLimiter.Perform(async () => JsonConvert.DeserializeObject<T>((await _http.GeneralRequestAsync(url, "PATCH", payload, api, clientId, accessToken).ConfigureAwait(false)).Value, _twitchLibJsonDeserializer)).ConfigureAwait(false);
}

protected async Task<KeyValuePair<int, string>> TwitchPatchAsync(string resource, ApiVersion api, string payload, List<KeyValuePair<string, string>> getParams = null, string accessToken = null, string clientId = null, string customBase = null)
protected async Task<KeyValuePair<int, string>> TwitchPatchAsync(string resource, ApiVersion api, string payload, List<KeyValuePair<string, string>>? getParams = null, string? accessToken = null, string? clientId = null, string? customBase = null)
{
var url = ConstructResourceUrl(resource, getParams, api, customBase);

Expand All @@ -126,7 +127,7 @@ protected async Task<KeyValuePair<int, string>> TwitchPatchAsync(string resource
return await _rateLimiter.Perform(async () => (await _http.GeneralRequestAsync(url, "PATCH", payload, api, clientId, accessToken).ConfigureAwait(false))).ConfigureAwait(false);
}

protected async Task<KeyValuePair<int, string>> TwitchDeleteAsync(string resource, ApiVersion api, List<KeyValuePair<string, string>> getParams = null, string accessToken = null, string clientId = null, string customBase = null)
protected async Task<KeyValuePair<int, string>> TwitchDeleteAsync(string resource, ApiVersion api, List<KeyValuePair<string, string>>? getParams = null, string? accessToken = null, string? clientId = null, string? customBase = null)
{
var url = ConstructResourceUrl(resource, getParams, api, customBase);

Expand All @@ -139,7 +140,7 @@ protected async Task<KeyValuePair<int, string>> TwitchDeleteAsync(string resourc
return await _rateLimiter.Perform(async () => (await _http.GeneralRequestAsync(url, "DELETE", null, api, clientId, accessToken).ConfigureAwait(false))).ConfigureAwait(false);
}

protected async Task<T> TwitchPostGenericAsync<T>(string resource, ApiVersion api, string payload, List<KeyValuePair<string, string>> getParams = null, string accessToken = null, string clientId = null, string customBase = null)
protected async Task<T> TwitchPostGenericAsync<T>(string resource, ApiVersion api, string payload, List<KeyValuePair<string, string>>? getParams = null, string? accessToken = null, string? clientId = null, string? customBase = null)
{
var url = ConstructResourceUrl(resource, getParams, api, customBase);

Expand All @@ -152,7 +153,7 @@ protected async Task<T> TwitchPostGenericAsync<T>(string resource, ApiVersion ap
return await _rateLimiter.Perform(async () => JsonConvert.DeserializeObject<T>((await _http.GeneralRequestAsync(url, "POST", payload, api, clientId, accessToken).ConfigureAwait(false)).Value, _twitchLibJsonDeserializer)).ConfigureAwait(false);
}

protected async Task<T> TwitchPostGenericModelAsync<T>(string resource, ApiVersion api, RequestModel model, string accessToken = null, string clientId = null, string customBase = null)
protected async Task<T> TwitchPostGenericModelAsync<T>(string resource, ApiVersion api, RequestModel? model, string? accessToken = null, string? clientId = null, string? customBase = null)
{
var url = ConstructResourceUrl(resource, api: api, overrideUrl: customBase);

Expand All @@ -165,7 +166,7 @@ protected async Task<T> TwitchPostGenericModelAsync<T>(string resource, ApiVersi
return await _rateLimiter.Perform(async () => JsonConvert.DeserializeObject<T>((await _http.GeneralRequestAsync(url, "POST", model != null ? _jsonSerializer.SerializeObject(model) : "", api, clientId, accessToken).ConfigureAwait(false)).Value, _twitchLibJsonDeserializer)).ConfigureAwait(false);
}

protected async Task<T> TwitchDeleteGenericAsync<T>(string resource, ApiVersion api, List<KeyValuePair<string, string>> getParams = null, string accessToken = null, string clientId = null, string customBase = null)
protected async Task<T> TwitchDeleteGenericAsync<T>(string resource, ApiVersion api, List<KeyValuePair<string, string>>? getParams = null, string? accessToken = null, string? clientId = null, string? customBase = null)
{
var url = ConstructResourceUrl(resource, getParams, api, customBase);

Expand All @@ -178,7 +179,7 @@ protected async Task<T> TwitchDeleteGenericAsync<T>(string resource, ApiVersion
return await _rateLimiter.Perform(async () => JsonConvert.DeserializeObject<T>((await _http.GeneralRequestAsync(url, "DELETE", null, api, clientId, accessToken).ConfigureAwait(false)).Value, _twitchLibJsonDeserializer)).ConfigureAwait(false);
}

protected async Task<T> TwitchPutGenericAsync<T>(string resource, ApiVersion api, string payload = null, List<KeyValuePair<string, string>> getParams = null, string accessToken = null, string clientId = null, string customBase = null)
protected async Task<T> TwitchPutGenericAsync<T>(string resource, ApiVersion api, string? payload = null, List<KeyValuePair<string, string>>? getParams = null, string? accessToken = null, string? clientId = null, string? customBase = null)
{
var url = ConstructResourceUrl(resource, getParams, api, customBase);

Expand All @@ -191,7 +192,7 @@ protected async Task<T> TwitchPutGenericAsync<T>(string resource, ApiVersion api
return await _rateLimiter.Perform(async () => JsonConvert.DeserializeObject<T>((await _http.GeneralRequestAsync(url, "PUT", payload, api, clientId, accessToken).ConfigureAwait(false)).Value, _twitchLibJsonDeserializer)).ConfigureAwait(false);
}

protected async Task<string> TwitchPutAsync(string resource, ApiVersion api, string payload, List<KeyValuePair<string, string>> getParams = null, string accessToken = null, string clientId = null, string customBase = null)
protected async Task<string> TwitchPutAsync(string resource, ApiVersion api, string payload, List<KeyValuePair<string, string>>? getParams = null, string? accessToken = null, string? clientId = null, string? customBase = null)
{
var url = ConstructResourceUrl(resource, getParams, api, customBase);

Expand All @@ -204,7 +205,7 @@ protected async Task<string> TwitchPutAsync(string resource, ApiVersion api, str
return await _rateLimiter.Perform(async () => (await _http.GeneralRequestAsync(url, "PUT", payload, api, clientId, accessToken).ConfigureAwait(false)).Value).ConfigureAwait(false);
}

protected async Task<KeyValuePair<int, string>> TwitchPostAsync(string resource, ApiVersion api, string payload, List<KeyValuePair<string, string>> getParams = null, string accessToken = null, string clientId = null, string customBase = null)
protected async Task<KeyValuePair<int, string>> TwitchPostAsync(string resource, ApiVersion api, string payload, List<KeyValuePair<string, string>>? getParams = null, string? accessToken = null, string? clientId = null, string? customBase = null)
{
var url = ConstructResourceUrl(resource, getParams, api, customBase);

Expand All @@ -223,12 +224,12 @@ protected Task PutBytesAsync(string url, byte[] payload)
return _http.PutBytesAsync(url, payload);
}

internal Task<int> RequestReturnResponseCode(string url, string method, List<KeyValuePair<string, string>> getParams = null)
internal Task<int> RequestReturnResponseCode(string url, string method, List<KeyValuePair<string, string>>? getParams = null)
{
return _http.RequestReturnResponseCodeAsync(url, method, getParams);
}

protected async Task<T> GetGenericAsync<T>(string url, List<KeyValuePair<string, string>> getParams = null, string accessToken = null, ApiVersion api = ApiVersion.Helix, string clientId = null)
protected async Task<T> GetGenericAsync<T>(string url, List<KeyValuePair<string, string>>? getParams = null, string? accessToken = null, ApiVersion api = ApiVersion.Helix, string? clientId = null)
{
if (getParams != null)
{
Expand All @@ -250,7 +251,7 @@ protected async Task<T> GetGenericAsync<T>(string url, List<KeyValuePair<string,
return await _rateLimiter.Perform(async () => JsonConvert.DeserializeObject<T>((await _http.GeneralRequestAsync(url, "GET", null, api, clientId, accessToken).ConfigureAwait(false)).Value, _twitchLibJsonDeserializer)).ConfigureAwait(false);
}

internal Task<T> GetSimpleGenericAsync<T>(string url, List<KeyValuePair<string, string>> getParams = null)
internal Task<T> GetSimpleGenericAsync<T>(string url, List<KeyValuePair<string, string>>? getParams = null)
{
if (getParams != null)
{
Expand Down Expand Up @@ -314,37 +315,36 @@ protected override string ResolvePropertyName(string propertyName)
}
}

private string ConstructResourceUrl(string resource = null, List<KeyValuePair<string, string>> getParams = null, ApiVersion api = ApiVersion.Helix, string overrideUrl = null)
private string ConstructResourceUrl(string? resource = null, List<KeyValuePair<string, string>>? getParams = null, ApiVersion api = ApiVersion.Helix, string? overrideUrl = null)
{
var url = "";
var url = new StringBuilder();
if (overrideUrl == null)
{
if (resource == null)
throw new Exception("Cannot pass null resource with null override url");
switch (api)
{
case ApiVersion.Helix:
url = $"{BaseHelix}{resource}";
url.Append($"{BaseHelix}{resource}");
break;
case ApiVersion.Auth:
url = $"{BaseAuth}{resource}";
url.Append($"{BaseAuth}{resource}");
break;
}
}
else
{
url = resource == null ? overrideUrl : $"{overrideUrl}{resource}";
url.Append(resource == null ? overrideUrl : $"{overrideUrl}{resource}");
}
if (getParams != null)
if (getParams?.Count > 0)
{
for (var i = 0; i < getParams.Count; i++)
var queryStartIndex = url.Length;
foreach (var param in getParams)
{
if (i == 0)
url += $"?{getParams[i].Key}={Uri.EscapeDataString(getParams[i].Value)}";
else
url += $"&{getParams[i].Key}={Uri.EscapeDataString(getParams[i].Value)}";
url.Append($"&{param.Key}={Uri.EscapeDataString(param.Value)}");
}
url[queryStartIndex] = '?';
}
return url;
return url.ToString();
}
}
38 changes: 31 additions & 7 deletions TwitchLib.Api.Core/Common/Helpers.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Buffers;
using System.Text;
using TwitchLib.Api.Core.Enums;

Expand All @@ -14,7 +15,15 @@ public static class Helpers
/// <returns></returns>
public static string FormatOAuth(string token)
{
#if NET
Span<Range> ranges = stackalloc Range[3];
var tokenSpan = token.AsSpan();
return tokenSpan.Split(ranges, ' ') is 1
? token
: tokenSpan[ranges[1]].ToString();
#else
return token.Contains(" ") ? token.Split(' ')[1] : token;
#endif
}

/// <summary>
Expand Down Expand Up @@ -100,13 +109,13 @@ public static string AuthScopesToString(AuthScopes scope)
AuthScopes.Moderator_Read_Banned_Users => "moderator:manage:banned_users",
AuthScopes.Moderator_Read_Chat_Messages => "moderator:read:chat_messages",
AuthScopes.Moderator_Read_Moderators => "moderator:read:moderators",
AuthScopes.Moderator_Read_Suspicious_Users=>"moderator:read:suspicious_users",
AuthScopes.Moderator_Read_Unban_Requests=>"moderator:read:unban_requests",
AuthScopes.Moderator_Read_VIPs=>"moderator:read:vips",
AuthScopes.Moderator_Read_Warnings=>"moderator:read:warnings",
AuthScopes.User_Edit_Broadcast=>"user:edit:broadcast",
AuthScopes.User_Read_Emotes=>"user:read:emotes",
AuthScopes.User_Read_Whispers=>"user:read:whispers",
AuthScopes.Moderator_Read_Suspicious_Users => "moderator:read:suspicious_users",
AuthScopes.Moderator_Read_Unban_Requests => "moderator:read:unban_requests",
AuthScopes.Moderator_Read_VIPs => "moderator:read:vips",
AuthScopes.Moderator_Read_Warnings => "moderator:read:warnings",
AuthScopes.User_Edit_Broadcast => "user:edit:broadcast",
AuthScopes.User_Read_Emotes => "user:read:emotes",
AuthScopes.User_Read_Whispers => "user:read:whispers",
AuthScopes.Any => string.Empty,
AuthScopes.None => string.Empty,
_ => string.Empty
Expand Down Expand Up @@ -213,7 +222,22 @@ public static AuthScopes StringToScope(string scope)
/// <returns>input as Base64 string</returns>
public static string Base64Encode(string plainText)
{
#if NET
var bytesLen = Encoding.UTF8.GetByteCount(plainText);
byte[]? array = null;
Span<byte> plainTextBytes = bytesLen <= 256
? stackalloc byte[bytesLen]
: (array = ArrayPool<byte>.Shared.Rent(bytesLen));

Encoding.UTF8.GetBytes(plainText, plainTextBytes);
var base64 = Convert.ToBase64String(plainTextBytes.Slice(0, bytesLen));

if (array is not null)
ArrayPool<byte>.Shared.Return(array);
return base64;
#else
var plainTextBytes = Encoding.UTF8.GetBytes(plainText);
return Convert.ToBase64String(plainTextBytes);
#endif
}
}
Loading
Loading