Skip to content
Open
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
33 changes: 33 additions & 0 deletions ApiAccessLevel.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
namespace Tiltify
{
public enum ApiAccessLevel
{
None,
OAuth,
Public,
Private,
PublicPrivate,
}

public static class ApiAccessPath
{
public static string GetPath(ApiAccessLevel level, ApiVersion version)
{
// V3 doesn't add access level pathing
if (version == ApiVersion.V3)
return "";

switch (level)
{
case ApiAccessLevel.Public:
return "/api/public";
case ApiAccessLevel.Private:
return "/api/private";
case ApiAccessLevel.OAuth:
case ApiAccessLevel.None:
default:
return "";
}
}
}
}
55 changes: 42 additions & 13 deletions ApiBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,7 @@ namespace Tiltify
{
public class ApiBase
{
internal const string BaseTiltifyAPI = "https://tiltify.com/api";

private readonly ApiSettings settings;
protected ApiSettings settings;
private readonly IRateLimiter rateLimiter;
private readonly IHttpCallHandler http;

Expand All @@ -25,48 +23,79 @@ public ApiBase(ApiSettings settings, IRateLimiter rateLimiter, IHttpCallHandler
this.http = http;
}

protected async Task<string> TiltifyGetAsync(string resource, ApiVersion api, List<KeyValuePair<string, string>> getParams = null, string customBase = null)
public ApiVersion GetApiVersion() => settings.APIVersion;

protected async Task<string> TiltifyGetAsync(string resource, ApiVersion api, List<KeyValuePair<string, string>> getParams = null, string customBase = null, ApiAccessLevel access = ApiAccessLevel.Public)
{
var url = ConstructResourceUrl(resource, getParams, api, customBase);
var url = ConstructResourceUrl(resource, getParams, api, customBase, access);
var accessToken = settings.OAuthToken;

return await rateLimiter.Perform(async () => (await http.GeneralRequestAsync(url, "GET", null, api, accessToken).ConfigureAwait(false)).Value).ConfigureAwait(false);
}

protected async Task<T> TiltifyGetGenericAsync<T>(string resource, ApiVersion api, List<KeyValuePair<string, string>> getParams = null, string customBase = null)
protected async Task<T> TiltifyGetGenericAsync<T>(string resource, ApiVersion api, List<KeyValuePair<string, string>> getParams = null, string customBase = null, ApiAccessLevel access = ApiAccessLevel.Public)
{
var url = ConstructResourceUrl(resource, getParams, api, customBase);
var url = ConstructResourceUrl(resource, getParams, api, customBase, access);
var accessToken = settings.OAuthToken;

return await rateLimiter.Perform(async () => JsonConvert.DeserializeObject<T>((await http.GeneralRequestAsync(url, "GET", null, api, accessToken).ConfigureAwait(false)).Value, jsonDeserializer)).ConfigureAwait(false);
}

private string ConstructResourceUrl(string resource = null, List<KeyValuePair<string, string>> getParams = null, ApiVersion api = ApiVersion.V3, string overrideUrl = null)
protected async Task<T> TiltifyPostGenericAsync<T>(string resource, ApiVersion api, List<KeyValuePair<string, string>> getParams = null, string customBase = null, ApiAccessLevel access = ApiAccessLevel.Public)
{
var url = ConstructResourceUrl(resource, null, api, customBase, access);
string payload = "";

if (getParams != null)
{
Dictionary<string, string> converted = new Dictionary<string, string>();
// Fun conversion from a KVP to a Dictionary so it can convert properly via JSONConvert
getParams.ForEach((singleKvp) => converted.Add(singleKvp.Key, singleKvp.Value));
payload = JsonConvert.SerializeObject(converted);
}

string accessToken = string.Empty;
if (access != ApiAccessLevel.OAuth)
accessToken = settings.OAuthToken;

return await rateLimiter.Perform(async () => JsonConvert.DeserializeObject<T>((await http.GeneralRequestAsync(url, "POST", payload, api, accessToken).ConfigureAwait(false)).Value, jsonDeserializer)).ConfigureAwait(false);
}

private string ConstructResourceUrl(string resource = null, List<KeyValuePair<string, string>> getParams = null, ApiVersion api = ApiVersion.Latest, string overrideUrl = null, ApiAccessLevel access = ApiAccessLevel.Public)
{
var url = "";
if (overrideUrl == null)
{
if (resource == null)
throw new Exception("Cannot pass null resource with null override url");

string accessPath = ApiAccessPath.GetPath(access, api);
switch (api)
{
case ApiVersion.V3:
url = $"{BaseTiltifyAPI}/v3{resource}";
url = $"https://tiltify.com/api/v3{resource}";
break;
case ApiVersion.V5:
url = $"https://v5api.tiltify.com{accessPath}{resource}";
break;
}
}
else
{
url = resource == null ? overrideUrl : $"{overrideUrl}{resource}";
}

if (getParams != null)
{
// Check to see if we have a ? and if not, we'll add the ?
if (!url.Contains('?'))
{
url += "?";
}

for (var i = 0; i < getParams.Count; i++)
{
if (i == 0)
url += $"?{getParams[i].Key}={Uri.EscapeDataString(getParams[i].Value)}";
else
url += $"&{getParams[i].Key}={Uri.EscapeDataString(getParams[i].Value)}";
url += $"&{getParams[i].Key}={Uri.EscapeDataString(getParams[i].Value)}";
}
}
return url;
Expand Down
5 changes: 5 additions & 0 deletions ApiSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@
{
public class ApiSettings
{
public string ClientID { get; set; } = string.Empty;
public string ClientSecret { get; set; } = string.Empty;
public string RefreshToken { get; set; } = string.Empty;
public string OAuthToken { get; set; }

public ApiVersion APIVersion {get; set;} = ApiVersion.Latest;
}
}
2 changes: 2 additions & 0 deletions ApiVersion.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,7 @@
public enum ApiVersion
{
V3 = 3,
V5 = 5,
Latest = V5,
}
}
83 changes: 83 additions & 0 deletions Auth.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using Tiltify.Models;

namespace Tiltify
{
public class Auth : ApiBase
{
public enum AuthGrantType
{
None,
ClientCredentials,
AuthorizationCode,
RefreshToken
}

public string GrantTypeToString(AuthGrantType grantType)
{
switch(grantType)
{
default:
case AuthGrantType.None:
return "";
case AuthGrantType.ClientCredentials:
return "client_credentials";
case AuthGrantType.AuthorizationCode:
return "authorization_code";
case AuthGrantType.RefreshToken:
return "refresh_token";
}
}

public Auth(ApiSettings settings, IRateLimiter rateLimiter, IHttpCallHandler http) : base(settings, rateLimiter, http)
{
}

public Task<AuthorizationResponse> Authorize(AuthGrantType grantType = AuthGrantType.ClientCredentials, ApiAccessLevel scopes = ApiAccessLevel.Public, string authCode = "")
{
List<KeyValuePair<string, string>> Args = new List<KeyValuePair<string, string>>();
Args.Add(new KeyValuePair<string, string>("grant_type", GrantTypeToString(grantType)));
Args.Add(new KeyValuePair<string, string>("client_id", settings.ClientID));
Args.Add(new KeyValuePair<string, string>("client_secret", settings.ClientSecret));

if (grantType == AuthGrantType.AuthorizationCode)
{
Args.Add(new KeyValuePair<string, string>("code", authCode));
}
else if (grantType == AuthGrantType.RefreshToken)
{
Args.Add(new KeyValuePair<string, string>("refresh_token", settings.RefreshToken));
}

// Adding the scopes
string scopeForm;
switch(scopes)
{
default:
case ApiAccessLevel.Public:
scopeForm = "public";
break;
case ApiAccessLevel.Private:
scopeForm = "webhooks:write";
break;
case ApiAccessLevel.PublicPrivate:
scopeForm = "public webhooks:write";
break;
}
Args.Add(new KeyValuePair<string, string>("scope", scopeForm));

Task<AuthorizationResponse> authResp = TiltifyPostGenericAsync<AuthorizationResponse>("/oauth/token", GetApiVersion(), Args, null, ApiAccessLevel.OAuth);
if (authResp.Result != null)
{
AuthorizationResponse resp = authResp.Result;
if (resp.Type == "bearer")
settings.OAuthToken = resp.AccessToken;

if (resp.RefreshToken != null)
settings.RefreshToken = resp.RefreshToken;
}
return authResp;
}
}
}
21 changes: 18 additions & 3 deletions Campaigns.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
using System.Threading.Tasks;
using System.Collections.Generic;
using System.Threading.Tasks;
using Tiltify.Models;
using System;
using System.Globalization;

namespace Tiltify
{
Expand All @@ -9,9 +12,21 @@ public Campaigns(ApiSettings settings, IRateLimiter rateLimiter, IHttpCallHandle
{
}

public Task<GetCampaignDonationsResponse> GetCampaignDonations(int campaignId)
public Task<GetCampaignDonationsResponse> GetCampaignDonations(int campaignId, ApiAccessLevel access = ApiAccessLevel.Public)
{
return TiltifyGetGenericAsync<GetCampaignDonationsResponse>($"/campaigns/{campaignId}/donations", ApiVersion.V3);
return TiltifyGetGenericAsync<GetCampaignDonationsResponse>($"/campaigns/{campaignId}/donations", ApiVersion.V3, null, null, access);
}

public Task<GetCampaignDonationsResponse> GetCampaignDonations(string campaignId, ApiAccessLevel access = ApiAccessLevel.Public)
{
return TiltifyGetGenericAsync<GetCampaignDonationsResponse>($"/campaigns/{campaignId}/donations", GetApiVersion(), null, null, access);
}

public Task<GetCampaignDonationsResponse> GetCampaignDonations(string campaignId, DateTime donationsAfter, int limit = 10, ApiAccessLevel access = ApiAccessLevel.Public)
{
List<KeyValuePair<string, string>> Args = new List<KeyValuePair<string, string>>();
Args.Add(new KeyValuePair<string, string>("completed_after", donationsAfter.ToString("o", CultureInfo.InvariantCulture)));
return TiltifyGetGenericAsync<GetCampaignDonationsResponse>($"/campaigns/{campaignId}/donations?limit={limit}", GetApiVersion(), Args, null, access);
}
}
}
2 changes: 1 addition & 1 deletion Events/OnCampaignDonationArgs.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,6 @@ namespace Tiltify.Events
{
public class OnCampaignDonationArgs : EventArgs
{
public DonationInformation Donation;
public WebSocketDonationInformation Donation;
}
}
2 changes: 1 addition & 1 deletion IHttpCallHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ namespace Tiltify
{
public interface IHttpCallHandler
{
Task<KeyValuePair<int, string>> GeneralRequestAsync(string url, string method, string payload = null, ApiVersion api = ApiVersion.V3, string accessToken = null);
Task<KeyValuePair<int, string>> GeneralRequestAsync(string url, string method, string payload = null, ApiVersion api = ApiVersion.Latest, string accessToken = null);
Task PutBytesAsync(string url, byte[] payload);
Task<int> RequestReturnResponseCodeAsync(string url, string method, List<KeyValuePair<string, string>> getParams = null);
}
Expand Down
25 changes: 25 additions & 0 deletions Models/AuthorizationResponse.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
using Newtonsoft.Json;

namespace Tiltify.Models
{
public class AuthorizationResponse
{
[JsonProperty(PropertyName = "access_token")]
public string AccessToken;

[JsonProperty(PropertyName = "created_at")]
public string CreationTime;

[JsonProperty(PropertyName = "expires_in")]
public int Expiration;
#nullable enable
[JsonProperty(PropertyName = "refresh_token")]
public string? RefreshToken;
#nullable restore
[JsonProperty(PropertyName = "scope")]
public string Scope;

[JsonProperty(PropertyName = "token_type")]
public string Type;
}
}
40 changes: 24 additions & 16 deletions Models/DonationInformation.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,28 +3,36 @@

namespace Tiltify.Models
{
//{ "amount":1.0,"challenge_id":null,"comment":null,"completedAt":1650338036000,"event_id":165720,"id":5842193,"name":"Anonymous","poll_option_id":null,"reward_id":null,"updatedAt":1650338036000}
public class Money
{
[JsonProperty(PropertyName = "currency")]
public string Currency = string.Empty;

[JsonProperty(PropertyName = "value")]
public string Value = string.Empty;
}

public class DonationInformation
{
[JsonProperty(PropertyName = "id")]
public int Id { get; protected set; }
[JsonProperty(PropertyName = "event_id")]
public int? CampaignId { get; protected set; }
[JsonProperty(PropertyName = "challenge_id")]
public int? ChallengeId { get; protected set; }
public string Id { get; protected set; }
#nullable enable
[JsonProperty(PropertyName = "campaign_id")]
public string? CampaignId { get; protected set; }

[JsonProperty(PropertyName = "poll_option_id")]
public int? PollOptionId { get; protected set; }
public string? PollOptionId { get; protected set; }

[JsonProperty(PropertyName = "amount")]
public double Amount { get; protected set; }
[JsonProperty(PropertyName = "name")]
public Money? Amount { get; protected set; }
#nullable restore
[JsonProperty(PropertyName = "donor_name")]
public string Name { get; protected set; }
[JsonProperty(PropertyName = "comment")]

[JsonProperty(PropertyName = "donor_comment")]
public string Comment { get; protected set; }
[JsonProperty(PropertyName = "completedAt")]
public long CompletedAt { get; protected set; }
[JsonProperty(PropertyName = "reward_id")]
public int? RewardId { get; protected set; }
[JsonProperty(PropertyName = "rewardId")]
private int? RewardIdAltKey { set { RewardId = value; } }

[JsonProperty(PropertyName = "completed_at")]
public string CompletedAt { get; protected set; }
}
}
10 changes: 10 additions & 0 deletions Models/GetTeamCampaignResponse.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
using Newtonsoft.Json;

namespace Tiltify.Models
{
public class GetTeamCampaignResponse
{
[JsonProperty(PropertyName = "data")]
public TeamCampaignInfo Data { get; protected set; }
}
}
Loading