psuedoinitial
actually I had a previous repo but my dumb ass let the real appsettings sneak in
This commit is contained in:
parent
33898cb456
commit
3f717df1d4
1
.gitignore
vendored
1
.gitignore
vendored
@ -373,3 +373,4 @@ FodyWeavers.xsd
|
|||||||
# Local History for Visual Studio Code
|
# Local History for Visual Studio Code
|
||||||
.history/
|
.history/
|
||||||
|
|
||||||
|
appsettings.json
|
||||||
|
26
.vscode/launch.json
vendored
Normal file
26
.vscode/launch.json
vendored
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
{
|
||||||
|
"version": "0.2.0",
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
// Use IntelliSense to find out which attributes exist for C# debugging
|
||||||
|
// Use hover for the description of the existing attributes
|
||||||
|
// For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md
|
||||||
|
"name": ".NET Core Launch (console)",
|
||||||
|
"type": "coreclr",
|
||||||
|
"request": "launch",
|
||||||
|
"preLaunchTask": "build",
|
||||||
|
// If you have changed target frameworks, make sure to update the program path.
|
||||||
|
"program": "${workspaceFolder}/bin/Debug/net5.0/twitcher.dll",
|
||||||
|
"args": [],
|
||||||
|
"cwd": "${workspaceFolder}",
|
||||||
|
// For more information about the 'console' field, see https://aka.ms/VSCode-CS-LaunchJson-Console
|
||||||
|
"console": "internalConsole",
|
||||||
|
"stopAtEntry": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": ".NET Core Attach",
|
||||||
|
"type": "coreclr",
|
||||||
|
"request": "attach"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
42
.vscode/tasks.json
vendored
Normal file
42
.vscode/tasks.json
vendored
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
{
|
||||||
|
"version": "2.0.0",
|
||||||
|
"tasks": [
|
||||||
|
{
|
||||||
|
"label": "build",
|
||||||
|
"command": "dotnet",
|
||||||
|
"type": "process",
|
||||||
|
"args": [
|
||||||
|
"build",
|
||||||
|
"${workspaceFolder}/twitcher.csproj",
|
||||||
|
"/property:GenerateFullPaths=true",
|
||||||
|
"/consoleloggerparameters:NoSummary"
|
||||||
|
],
|
||||||
|
"problemMatcher": "$msCompile"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "publish",
|
||||||
|
"command": "dotnet",
|
||||||
|
"type": "process",
|
||||||
|
"args": [
|
||||||
|
"publish",
|
||||||
|
"${workspaceFolder}/twitcher.csproj",
|
||||||
|
"/property:GenerateFullPaths=true",
|
||||||
|
"/consoleloggerparameters:NoSummary"
|
||||||
|
],
|
||||||
|
"problemMatcher": "$msCompile"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "watch",
|
||||||
|
"command": "dotnet",
|
||||||
|
"type": "process",
|
||||||
|
"args": [
|
||||||
|
"watch",
|
||||||
|
"run",
|
||||||
|
"${workspaceFolder}/twitcher.csproj",
|
||||||
|
"/property:GenerateFullPaths=true",
|
||||||
|
"/consoleloggerparameters:NoSummary"
|
||||||
|
],
|
||||||
|
"problemMatcher": "$msCompile"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
12
Config.cs
Normal file
12
Config.cs
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
namespace twitcher
|
||||||
|
{
|
||||||
|
public class Config
|
||||||
|
{
|
||||||
|
public string kafka_bootstrap { get; set; }
|
||||||
|
public string kafka_name { get; set; }
|
||||||
|
public int port { get; set; }
|
||||||
|
public string clientId { get; set; }
|
||||||
|
public string clientSecret { get; set; }
|
||||||
|
public string public_uri { get; set; }
|
||||||
|
}
|
||||||
|
}
|
40
OAuthTokenGetter.cs
Normal file
40
OAuthTokenGetter.cs
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
using Newtonsoft.Json;
|
||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Net.Http;
|
||||||
|
|
||||||
|
public class OAuthTokenGetter
|
||||||
|
{
|
||||||
|
public static OAuthToken DoIt(HttpClient client, string clientId, string clientSecret, string[] scopes = null)
|
||||||
|
{
|
||||||
|
var scopeString = "";
|
||||||
|
if (scopes != null && scopes.Length > 0)
|
||||||
|
{
|
||||||
|
scopeString = "&scope=" + string.Join(' ', scopes);
|
||||||
|
}
|
||||||
|
var postURI = $"https://id.twitch.tv/oauth2/token?client_id={clientId}&client_secret={clientSecret}&grant_type=client_credentials{scopeString}";
|
||||||
|
//Console.WriteLine(postURI);
|
||||||
|
var r = client.PostAsync(postURI, null).Result;
|
||||||
|
|
||||||
|
var response = "";
|
||||||
|
using (var streamReader = new StreamReader(r.Content.ReadAsStream()))
|
||||||
|
{
|
||||||
|
response = streamReader.ReadToEnd();
|
||||||
|
}
|
||||||
|
if(!r.IsSuccessStatusCode)
|
||||||
|
{
|
||||||
|
Console.Error.WriteLine(response);
|
||||||
|
throw new Exception(response);
|
||||||
|
}
|
||||||
|
//Console.WriteLine(response);
|
||||||
|
return JsonConvert.DeserializeObject<OAuthToken>(response);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public class OAuthToken
|
||||||
|
{
|
||||||
|
public string access_token { get; set; }
|
||||||
|
public string refresh_token { get; set; }
|
||||||
|
public int expires_in { get; set; }
|
||||||
|
public string[] scope { get; set; }
|
||||||
|
public string token_type { get; set; }
|
||||||
|
}
|
51
Program.cs
Normal file
51
Program.cs
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
using franz;
|
||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using TwitchEventSub.Types.EventSubSubscription;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace twitcher
|
||||||
|
{
|
||||||
|
class Program
|
||||||
|
{
|
||||||
|
public static TwitchEventSub.Receiver httpd;
|
||||||
|
public static Config twitcherConf;
|
||||||
|
//public static Telefranz tf;
|
||||||
|
static void Main(string[] args)
|
||||||
|
{
|
||||||
|
JsonConvert.DefaultSettings = () => {
|
||||||
|
var s = new JsonSerializerSettings();
|
||||||
|
s.Converters.Add(new Newtonsoft.Json.Converters.StringEnumConverter());
|
||||||
|
return s;
|
||||||
|
};
|
||||||
|
if (!File.Exists("appsettings.json"))
|
||||||
|
{
|
||||||
|
Console.Error.WriteLine("appsettings.json was not found!");
|
||||||
|
twitcherConf = new Config();
|
||||||
|
File.WriteAllText("appsettings.json", JsonConvert.SerializeObject(twitcherConf, Formatting.Indented));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
twitcherConf = JsonConvert.DeserializeObject<Config>(File.ReadAllText("appsettings.json"));
|
||||||
|
|
||||||
|
// Telefranz.Configure(name: twitcherConf.kafka_name, bootstrap_servers: twitcherConf.kafka_bootstrap);
|
||||||
|
// Task.WaitAll(Task.Delay(1000));
|
||||||
|
// tf = Telefranz.Instance;
|
||||||
|
|
||||||
|
//TODO: throw a request out somewhere asking for public url
|
||||||
|
//tf.ProduceMessage(new silver_messages.directorial.execute_command(){command = "ssl_expose", args = {twitcherConf.port.ToString()}});
|
||||||
|
|
||||||
|
httpd = new TwitchEventSub.Receiver(twitcherConf.port, twitcherConf.clientId, twitcherConf.clientSecret, twitcherConf.public_uri);
|
||||||
|
Task.WaitAll(
|
||||||
|
httpd.go(),
|
||||||
|
Task.Run(() => {httpd.Subscribe(SubscribableTypes.channel_follow,
|
||||||
|
new TwitchEventSub.Types.Conditions.ChannelFollow() { broadcaster_user_id = "12826" },
|
||||||
|
(tg) => {
|
||||||
|
Console.WriteLine("I'm the handler for a twitchogram!");
|
||||||
|
Console.WriteLine(JsonConvert.SerializeObject(tg));
|
||||||
|
}
|
||||||
|
);})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
269
TwitchEventSub/Receiver.cs
Normal file
269
TwitchEventSub/Receiver.cs
Normal file
@ -0,0 +1,269 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Net;
|
||||||
|
using System.Net.Http;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using TwitchEventSub.Types.EventSubSubscription;
|
||||||
|
using System.Security.Cryptography;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace TwitchEventSub
|
||||||
|
{
|
||||||
|
public class Receiver
|
||||||
|
{
|
||||||
|
private OAuthToken token;
|
||||||
|
private HttpListener server;
|
||||||
|
static readonly HttpClient client = new HttpClient();
|
||||||
|
public Dictionary<Subscription, Action<Twitchogram>> psuedoResources { get; set; }
|
||||||
|
= new Dictionary<Subscription, Action<Twitchogram>>();
|
||||||
|
private string hmacSecret = "";
|
||||||
|
private bool going = false;
|
||||||
|
private Uri publicUrl;
|
||||||
|
private Queue<string> twitchogrambuffer { get; set; }
|
||||||
|
private string clientId;
|
||||||
|
private HMACSHA256 hmacsha256 = null;
|
||||||
|
private List<Task> tasksToNotActuallyAwait = new List<Task>();
|
||||||
|
public Receiver(int port, string clientId, string clientSecret, string publicUrl)
|
||||||
|
{
|
||||||
|
this.clientId = clientId;
|
||||||
|
this.publicUrl = new Uri(publicUrl + "/twitcherize");
|
||||||
|
twitchogrambuffer = new Queue<string>();
|
||||||
|
server = new HttpListener();
|
||||||
|
server.Prefixes.Add($"http://*:{port}/");
|
||||||
|
|
||||||
|
authorizeClient(clientId, clientSecret);
|
||||||
|
|
||||||
|
clearOldSubscriptions();
|
||||||
|
|
||||||
|
prepareHmac();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void prepareHmac()
|
||||||
|
{
|
||||||
|
var r = new Random();
|
||||||
|
var stringLength = r.Next(20, 100);
|
||||||
|
var charChoices = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
|
||||||
|
for (int i = 0; i < stringLength; i++)
|
||||||
|
{
|
||||||
|
this.hmacSecret += charChoices[r.Next(charChoices.Length)];
|
||||||
|
}
|
||||||
|
this.hmacsha256 = new HMACSHA256(Encoding.ASCII.GetBytes(this.hmacSecret));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void authorizeClient(string clientId, string clientSecret)
|
||||||
|
{
|
||||||
|
this.token = OAuthTokenGetter.DoIt(client, clientId, clientSecret);
|
||||||
|
Console.WriteLine($"got a token");
|
||||||
|
client.DefaultRequestHeaders.Add("Client-ID", $"{this.clientId }");
|
||||||
|
client.DefaultRequestHeaders.Add("Authorization", $"Bearer {this.token.access_token}");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void clearOldSubscriptions()
|
||||||
|
{
|
||||||
|
var r = client.GetAsync("https://api.twitch.tv/helix/eventsub/subscriptions").Result;
|
||||||
|
|
||||||
|
var responseText = "";
|
||||||
|
using (var streamReader = new StreamReader(r.Content.ReadAsStream()))
|
||||||
|
{
|
||||||
|
responseText = streamReader.ReadToEnd();
|
||||||
|
}
|
||||||
|
if(!r.IsSuccessStatusCode)
|
||||||
|
{
|
||||||
|
Console.Error.WriteLine(responseText);
|
||||||
|
throw new Exception(responseText);
|
||||||
|
}
|
||||||
|
|
||||||
|
var listing = SubscribableTypesTranslation.RefineTwitchResponse(responseText);
|
||||||
|
|
||||||
|
var deletes = new List<Task<HttpResponseMessage>>();
|
||||||
|
foreach(var subscription in listing.data)
|
||||||
|
{
|
||||||
|
deletes.Add(client.DeleteAsync($"https://api.twitch.tv/helix/eventsub/subscriptions?id={subscription.id}"));
|
||||||
|
}
|
||||||
|
Task.WaitAll(deletes.ToArray());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Subscribe<c>(SubscribableTypes type, c condition, Action<Twitchogram> handler)
|
||||||
|
where c : Types.Conditions.Condition
|
||||||
|
{
|
||||||
|
if (!going)
|
||||||
|
{
|
||||||
|
Task.Run(go);
|
||||||
|
}
|
||||||
|
var subreq = new Request()
|
||||||
|
{
|
||||||
|
condition = condition,
|
||||||
|
transport = new Transport()
|
||||||
|
{
|
||||||
|
callback = publicUrl,
|
||||||
|
secret = hmacSecret
|
||||||
|
},
|
||||||
|
type = SubscribableTypesTranslation.Enum2String(type),
|
||||||
|
version = "1"
|
||||||
|
};
|
||||||
|
var subAsString = JsonConvert.SerializeObject(subreq);
|
||||||
|
var content = new StringContent(subAsString, Encoding.UTF8, "application/json");
|
||||||
|
var r = client.PostAsync("https://api.twitch.tv/helix/eventsub/subscriptions", content).Result;
|
||||||
|
|
||||||
|
string responseText;
|
||||||
|
using (var streamReader = new StreamReader(r.Content.ReadAsStream()))
|
||||||
|
{
|
||||||
|
responseText = streamReader.ReadToEnd();
|
||||||
|
}
|
||||||
|
var tr = SubscribableTypesTranslation.RefineTwitchResponse(responseText);
|
||||||
|
|
||||||
|
var firstSubscription = tr.data?.FirstOrDefault();
|
||||||
|
if(firstSubscription != null)
|
||||||
|
{
|
||||||
|
psuedoResources.Add(firstSubscription, handler);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public async Task go()
|
||||||
|
{
|
||||||
|
going = true;
|
||||||
|
server.Start();
|
||||||
|
await Task.Run(() =>
|
||||||
|
{
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
HttpListenerContext context = server.GetContext();
|
||||||
|
HttpListenerResponse response = context.Response;
|
||||||
|
|
||||||
|
string page = context.Request.Url.LocalPath.Substring(1);
|
||||||
|
Console.WriteLine($"{DateTime.Now.ToShortTimeString()} page requested: {page}");
|
||||||
|
var resp = "no resource";
|
||||||
|
response.StatusCode = (int)HttpStatusCode.NotFound;
|
||||||
|
lock (psuedoResources)
|
||||||
|
{
|
||||||
|
if (page == "twitcherize")
|
||||||
|
{
|
||||||
|
byte[] rawIncomingBytes = new byte[context.Request.ContentLength64];
|
||||||
|
var readBytes = context.Request.InputStream.Read(rawIncomingBytes, 0, (int)context.Request.ContentLength64);
|
||||||
|
|
||||||
|
|
||||||
|
if (VerifySignature(context.Request.Headers["Twitch-Eventsub-Message-Signature"],
|
||||||
|
context.Request.ContentEncoding,
|
||||||
|
context.Request.Headers["Twitch-Eventsub-Message-Id"],
|
||||||
|
context.Request.Headers["Twitch-Eventsub-Message-Timestamp"],
|
||||||
|
rawIncomingBytes))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (twitchogrambuffer.Contains(context.Request.Headers["Twitch-Eventsub-Message-Id"]))
|
||||||
|
{
|
||||||
|
Console.WriteLine("duplicate message received. Ignoring.");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var incomingText = context.Request.ContentEncoding.GetString(rawIncomingBytes);
|
||||||
|
//Console.WriteLine($"{context.Request.Headers["Twitch-Eventsub-Message-Id"]} looks new to me.");
|
||||||
|
lock (twitchogrambuffer)
|
||||||
|
{
|
||||||
|
var fromTwitch = SubscribableTypesTranslation.RefineTwitchogram(incomingText);
|
||||||
|
if (fromTwitch.challenge != null)
|
||||||
|
{
|
||||||
|
resp = fromTwitch.challenge;
|
||||||
|
Console.WriteLine("challenge responded to, should be subscribed");
|
||||||
|
}
|
||||||
|
else if (fromTwitch.subscription.status == "authorization_revoked")
|
||||||
|
{
|
||||||
|
resp = "ok... :(";
|
||||||
|
Console.WriteLine($"auth revoked for {fromTwitch.subscription.type} ({fromTwitch.subscription.id})");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var foundAHandler = false;
|
||||||
|
foreach (var kvp in psuedoResources)
|
||||||
|
{
|
||||||
|
if (kvp.Key.id == fromTwitch.subscription.id)
|
||||||
|
{
|
||||||
|
kvp.Value(fromTwitch);
|
||||||
|
resp = ":)";
|
||||||
|
foundAHandler = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!foundAHandler)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"I don't have a handler for {fromTwitch.subscription.type} ({fromTwitch.subscription.id})? o_O");
|
||||||
|
resp = ":S";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tasksToNotActuallyAwait.Add(logMessageId(context.Request.Headers["Twitch-Eventsub-Message-Id"]));
|
||||||
|
response.StatusCode = (int)HttpStatusCode.OK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Console.Error.WriteLine("something went wrong calling resource.");
|
||||||
|
Console.Error.WriteLine(JsonConvert.SerializeObject(e));
|
||||||
|
response.StatusCode = (int)HttpStatusCode.InternalServerError;
|
||||||
|
resp = "error :(";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Console.WriteLine("couldn't verify signature.");
|
||||||
|
response.StatusCode = (int)HttpStatusCode.Forbidden;
|
||||||
|
resp = "VoteNay";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Console.WriteLine($"returning status code {response.StatusCode}");
|
||||||
|
byte[] buffer = Encoding.UTF8.GetBytes(resp);
|
||||||
|
response.ContentLength64 = buffer.Length;
|
||||||
|
Stream st = response.OutputStream;
|
||||||
|
st.Write(buffer, 0, buffer.Length);
|
||||||
|
response.Close();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
going = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task logMessageId(string id)
|
||||||
|
{
|
||||||
|
lock(twitchogrambuffer)
|
||||||
|
{
|
||||||
|
twitchogrambuffer.Enqueue(id);
|
||||||
|
}
|
||||||
|
await Task.Delay(2000);
|
||||||
|
string x;
|
||||||
|
lock(twitchogrambuffer)
|
||||||
|
{
|
||||||
|
twitchogrambuffer.TryDequeue(out x);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool VerifySignature(string signature, Encoding encoder, string messageId, string timestamp, byte[] rawIncomingBytes)
|
||||||
|
{
|
||||||
|
var hashables = new List<byte[]>();
|
||||||
|
hashables.Add(encoder.GetBytes(messageId + timestamp).Concat(rawIncomingBytes).ToArray());
|
||||||
|
hashables.Add(encoder.GetBytes(messageId.ToLower() + timestamp).Concat(rawIncomingBytes).ToArray());
|
||||||
|
hashables.Add(encoder.GetBytes(messageId.ToLower() + timestamp.ToLower()).Concat(rawIncomingBytes).ToArray());
|
||||||
|
hashables.Add(encoder.GetBytes(messageId + timestamp.ToLower()).Concat(rawIncomingBytes).ToArray());
|
||||||
|
var computeds = new List<string>();
|
||||||
|
foreach(var hashable in hashables)
|
||||||
|
{
|
||||||
|
computeds.Add("sha256=" + Convert.ToHexString(hmacsha256.ComputeHash(hashable)).ToLower());
|
||||||
|
// var hashed = computeds.Last();
|
||||||
|
// if(signature?.ToLower() == hashed)
|
||||||
|
// {
|
||||||
|
// Console.Write(" * ");
|
||||||
|
// }
|
||||||
|
// else
|
||||||
|
// {
|
||||||
|
// Console.Write(" ");
|
||||||
|
// }
|
||||||
|
// Console.WriteLine(computeds.Last());
|
||||||
|
}
|
||||||
|
return computeds.Contains(signature?.ToLower());
|
||||||
|
//Console.WriteLine($"{signature?.ToLower()} == sha256={computed}?");
|
||||||
|
//return signature?.ToLower() == $"sha256={computed}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
15
TwitchEventSub/Types/ChannelCheer.cs
Normal file
15
TwitchEventSub/Types/ChannelCheer.cs
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
namespace TwitchEventSub.Types
|
||||||
|
{
|
||||||
|
public class ChannelCheer : Event
|
||||||
|
{
|
||||||
|
public bool is_anonymous { get; set; } //Whether the user cheered anonymously or not.
|
||||||
|
public string user_id { get; set; } //The user ID for the user who cheered on the specified channel. This is null if is_anonymous is true.
|
||||||
|
public string user_login { get; set; } //The user login for the user who cheered on the specified channel. This is null if is_anonymous is true.
|
||||||
|
public string user_name { get; set; } //The user display name for the user who cheered on the specified channel. This is null if is_anonymous is true.
|
||||||
|
public string broadcaster_user_id { get; set; } //The requested broadcaster ID.
|
||||||
|
public string broadcaster_user_login { get; set; } //The requested broadcaster login.
|
||||||
|
public string broadcaster_user_name { get; set; } //The requested broadcaster display name.
|
||||||
|
public string message { get; set; } //The message sent with the cheer.
|
||||||
|
public int bits { get; set; } //The number of bits cheered.
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,20 @@
|
|||||||
|
namespace TwitchEventSub.Types.ChannelPoints
|
||||||
|
{
|
||||||
|
public class CustomChannelPointsReward
|
||||||
|
{
|
||||||
|
public string id { get; set; }
|
||||||
|
public string title { get; set; }
|
||||||
|
public int cost { get; set; }
|
||||||
|
public string prompt { get; set; } //The reward description.
|
||||||
|
}
|
||||||
|
public class GlobalCooldown
|
||||||
|
{
|
||||||
|
public bool is_enabled { get; set; }
|
||||||
|
public int seconds { get; set; }
|
||||||
|
}
|
||||||
|
public abstract class intSetting
|
||||||
|
{
|
||||||
|
public bool is_enabled { get; set; }
|
||||||
|
public int value { get; set; }
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,19 @@
|
|||||||
|
namespace TwitchEventSub.Types.ChannelPoints
|
||||||
|
{
|
||||||
|
public class ChannelPointsCustomRewardRedemption: Event
|
||||||
|
{
|
||||||
|
public string id { get; set; } //The redemption identifier.
|
||||||
|
public string broadcaster_user_id { get; set; } //The requested broadcaster ID.
|
||||||
|
public string broadcaster_user_login { get; set; } //The requested broadcaster login.
|
||||||
|
public string broadcaster_user_name { get; set; } //The requested broadcaster display name.
|
||||||
|
public string user_id { get; set; } //User ID of the user that redeemed the reward.
|
||||||
|
public string user_login { get; set; } //Login of the user that redeemed the reward.
|
||||||
|
public string user_name { get; set; } //Display name of the user that redeemed the reward.
|
||||||
|
public string user_input { get; set; } //The user input provided. Empty string if not provided.
|
||||||
|
public string status { get; set; } //add defaults to unfulfilled, update will be fulfilled or canceled. Possible values are unknown, unfulfilled, fulfilled, and canceled.
|
||||||
|
public CustomChannelPointsReward reward { get; set; } //Basic information about the reward that was redeemed, at the time it was redeemed.
|
||||||
|
public string redeemed_at { get; set; } //RFC3339 timestamp of when the reward was redeemed.
|
||||||
|
}
|
||||||
|
public class ChannelPointsCustomRewardRedemptionAdd : ChannelPointsCustomRewardRedemption {}
|
||||||
|
public class ChannelPointsCustomRewardRedemptionUpdate : ChannelPointsCustomRewardRedemption {}
|
||||||
|
}
|
@ -0,0 +1,28 @@
|
|||||||
|
namespace TwitchEventSub.Types.ChannelPoints
|
||||||
|
{
|
||||||
|
public class ChannelPointsCustomRewardUpdate : Event
|
||||||
|
{
|
||||||
|
public string id { get; set; } //The reward identifier.
|
||||||
|
public string broadcaster_user_id { get; set; } //The requested broadcaster ID.
|
||||||
|
public string broadcaster_user_login { get; set; } //The requested broadcaster login.
|
||||||
|
public string broadcaster_user_name { get; set; } //The requested broadcaster display name.
|
||||||
|
public bool is_enabled { get; set; } //Is the reward currently enabled. If false, the reward won’t show up to viewers.
|
||||||
|
public bool is_paused { get; set; } //Is the reward currently paused. If true, viewers can’t redeem.
|
||||||
|
public bool is_in_stock { get; set; } //Is the reward currently in stock. If false, viewers can’t redeem.
|
||||||
|
public string title { get; set; } //The reward title.
|
||||||
|
public int cost { get; set; } //The reward cost.
|
||||||
|
public string prompt { get; set; } //The reward description.
|
||||||
|
public bool is_user_input_required { get; set; } //Does the viewer need to enter information when redeeming the reward.
|
||||||
|
public bool should_redemptions_skip_request_queue { get; set; } //Should redemptions be set to fulfilled status immediately when redeemed and skip the request queue instead of the normal unfulfilled status.
|
||||||
|
public intSetting max_per_stream { get; set; } //Whether a maximum per stream is enabled and what the maximum is.
|
||||||
|
public intSetting max_per_user_per_stream { get; set; } //Whether a maximum per user per stream is enabled and what the maximum is.
|
||||||
|
public string background_color { get; set; } //Custom background color for the reward. Format: Hex with # prefix. Example: #FA1ED2.
|
||||||
|
public Image image { get; set; } //Set of custom images of 1x, 2x and 4x sizes for the reward. Can be null if no images have been uploaded.
|
||||||
|
public Image default_image { get; set; } //Set of default images of 1x, 2x and 4x sizes for the reward.
|
||||||
|
public GlobalCooldown global_cooldown { get; set; } //Whether a cooldown is enabled and what the cooldown is in seconds.
|
||||||
|
public string cooldown_expires_at { get; set; } //Timestamp of the cooldown expiration. null if the reward isn’t on cooldown.
|
||||||
|
public int redemptions_redeemed_current_stream { get; set; } //The number of redemptions redeemed during the current live stream. Counts against the max_per_stream limit. null if the broadcasters stream isn’t live or max_per_stream isn’t enabled.
|
||||||
|
}
|
||||||
|
public class ChannelPointsCustomRewardRemove : ChannelPointsCustomRewardUpdate{}
|
||||||
|
public class ChannelPointsCustomRewardAdd : ChannelPointsCustomRewardUpdate{}
|
||||||
|
}
|
11
TwitchEventSub/Types/ChannelPoints/Image.cs
Normal file
11
TwitchEventSub/Types/ChannelPoints/Image.cs
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace TwitchEventSub.Types
|
||||||
|
{
|
||||||
|
public class Image
|
||||||
|
{
|
||||||
|
public Uri url_1x { get; set; } //URL for the image at 1x size.
|
||||||
|
public Uri url_2x { get; set; }
|
||||||
|
public Uri url_4x { get; set; }
|
||||||
|
}
|
||||||
|
}
|
14
TwitchEventSub/Types/ChannelRaid.cs
Normal file
14
TwitchEventSub/Types/ChannelRaid.cs
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
|
||||||
|
namespace TwitchEventSub.Types
|
||||||
|
{
|
||||||
|
public class ChannelRaid : Event
|
||||||
|
{
|
||||||
|
public string from_broadcaster_user_id { get; set; } //The broadcaster ID that created the raid.
|
||||||
|
public string from_broadcaster_user_login { get; set; } //The broadcaster login that created the raid.
|
||||||
|
public string from_broadcaster_user_name { get; set; } //The broadcaster display name that created the raid.
|
||||||
|
public string to_broadcaster_user_id { get; set; } //The broadcaster ID that received the raid.
|
||||||
|
public string to_broadcaster_user_login { get; set; } //The broadcaster login that received the raid.
|
||||||
|
public string to_broadcaster_user_name { get; set; } //The broadcaster display name that received the raid.
|
||||||
|
public int viewers { get; set; } //The number of viewers in the raid.
|
||||||
|
}
|
||||||
|
}
|
14
TwitchEventSub/Types/ChannelSubscription/ChannelSubscribe.cs
Normal file
14
TwitchEventSub/Types/ChannelSubscription/ChannelSubscribe.cs
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
namespace TwitchEventSub.Types.ChannelSubscription
|
||||||
|
{
|
||||||
|
public class ChannelSubscribe : Event
|
||||||
|
{
|
||||||
|
public string user_id { get; set; } //The user ID for the user who subscribed to the specified channel.
|
||||||
|
public string user_login { get; set; } //The user login for the user who subscribed to the specified channel.
|
||||||
|
public string user_name { get; set; } //The user display name for the user who subscribed to the specified channel.
|
||||||
|
public string broadcaster_user_id { get; set; } //The requested broadcaster ID.
|
||||||
|
public string broadcaster_user_login { get; set; } //The requested broadcaster login.
|
||||||
|
public string broadcaster_user_name { get; set; } //The requested broadcaster display name.
|
||||||
|
public string tier { get; set; } //The tier of the subscription. Valid values are 1000, 2000, and 3000.
|
||||||
|
public bool is_gift { get; set; } //Whether the subscription is a gift.
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,15 @@
|
|||||||
|
namespace TwitchEventSub.Types.ChannelSubscription
|
||||||
|
{
|
||||||
|
//this event continues to be WILD. what POSSIBLE use can there be that isn't horrible?
|
||||||
|
public class ChannelSubscriptionEnd : Event
|
||||||
|
{
|
||||||
|
public string user_id { get; set; } //The user ID for the user whose subscription ended.
|
||||||
|
public string user_login { get; set; } //The user login for the user whose subscription ended.
|
||||||
|
public string user_name { get; set; } //The user display name for the user whose subscription ended.
|
||||||
|
public string broadcaster_user_id { get; set; } //The broadcaster user ID.
|
||||||
|
public string broadcaster_user_login { get; set; } //The broadcaster login.
|
||||||
|
public string broadcaster_user_name { get; set; } //The broadcaster display name.
|
||||||
|
public string tier { get; set; } //The tier of the subscription that ended. Valid values are 1000, 2000, and 3000.
|
||||||
|
public bool is_gift { get; set; } //Whether the subscription was a gift.
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,16 @@
|
|||||||
|
namespace TwitchEventSub.Types.ChannelSubscription
|
||||||
|
{
|
||||||
|
public class ChannelSubscriptionGift : Event
|
||||||
|
{
|
||||||
|
public string user_id { get; set; } //The user ID of the user who sent the subscription gift. Set to null if it was an anonymous subscription gift.
|
||||||
|
public string user_login { get; set; } //The user login of the user who sent the gift. Set to null if it was an anonymous subscription gift.
|
||||||
|
public string user_name { get; set; } //The user display name of the user who sent the gift. Set to null if it was an anonymous subscription gift.
|
||||||
|
public string broadcaster_user_id { get; set; } //The broadcaster user ID.
|
||||||
|
public string broadcaster_user_login { get; set; } //The broadcaster login.
|
||||||
|
public string broadcaster_user_name { get; set; } //The broadcaster display name.
|
||||||
|
public int total { get; set; } //The number of subscriptions in the subscription gift.
|
||||||
|
public string tier { get; set; } //The tier of subscriptions in the subscription gift.
|
||||||
|
public int cumulative_total { get; set; } //The number of subscriptions gifted by this user in the channel. This value is null for anonymous gifts or if the gifter has opted out of sharing this information.
|
||||||
|
public bool is_anonymous { get; set; } //Whether the subscription gift was anonymous.
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,28 @@
|
|||||||
|
namespace TwitchEventSub.Types.ChannelSubscription
|
||||||
|
{
|
||||||
|
public class ChannelSubscriptionMessage : Event
|
||||||
|
{
|
||||||
|
public string user_id { get; set; } //The user ID of the user who sent a resubscription chat message.
|
||||||
|
public string user_login { get; set; } //The user login of the user who sent a resubscription chat message.
|
||||||
|
public string user_name { get; set; } //The user display name of the user who a resubscription chat message.
|
||||||
|
public string broadcaster_user_id { get; set; } //The broadcaster user ID.
|
||||||
|
public string broadcaster_user_login { get; set; } //The broadcaster login.
|
||||||
|
public string broadcaster_user_name { get; set; } //The broadcaster display name.
|
||||||
|
public string tier { get; set; } //The tier of the user’s subscription.
|
||||||
|
public Message message { get; set; } //An object that contains the resubscription message and emote information needed to recreate the message.
|
||||||
|
public int cumulative_months { get; set; } //The total number of months the user has been subscribed to the channel.
|
||||||
|
public int streak_months { get; set; } //The number of consecutive months the user’s current subscription has been active. This value is null if the user has opted out of sharing this information.
|
||||||
|
public int duration_months { get; set; } //The month duration of the subscription.
|
||||||
|
}
|
||||||
|
public class Message
|
||||||
|
{
|
||||||
|
public string text { get; set; }
|
||||||
|
public Emote[] emotes { get; set; }
|
||||||
|
}
|
||||||
|
public class Emote
|
||||||
|
{
|
||||||
|
public int begin { get; set; } //where the Emote starts in the text.
|
||||||
|
public int end { get; set; } //where the Emote ends in the text.
|
||||||
|
public string id { get; set; }
|
||||||
|
}
|
||||||
|
}
|
22
TwitchEventSub/Types/DropEntitlementGrant.cs
Normal file
22
TwitchEventSub/Types/DropEntitlementGrant.cs
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
namespace TwitchEventSub.Types
|
||||||
|
{
|
||||||
|
public class DropEntitlementGrant : Event
|
||||||
|
{
|
||||||
|
public string id { get; set; } //Individual event ID, as assigned by EventSub. Use this for de-duplicating messages.
|
||||||
|
public EntitlementObject[] data { get; set; }
|
||||||
|
|
||||||
|
public class EntitlementObject
|
||||||
|
{
|
||||||
|
public string organization_id { get; set; } //The ID of the organization that owns the game that has Drops enabled.
|
||||||
|
public string category_id { get; set; } //Twitch category ID of the game that was being played when this benefit was entitled.
|
||||||
|
public string category_name { get; set; } //The category name.
|
||||||
|
public string campaign_id { get; set; } //The campaign this entitlement is associated with.
|
||||||
|
public string user_id { get; set; } //Twitch user ID of the user who was granted the entitlement.
|
||||||
|
public string user_name { get; set; } //The user display name of the user who was granted the entitlement.
|
||||||
|
public string user_login { get; set; } //The user login of the user who was granted the entitlement.
|
||||||
|
public string entitlement_id { get; set; } //Unique identifier of the entitlement. Use this to de-duplicate entitlements.
|
||||||
|
public string benefit_id { get; set; } //Identifier of the Benefit.
|
||||||
|
public string created_at { get; set; } //UTC timestamp in ISO format when this entitlement was granted on Twitch.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
4
TwitchEventSub/Types/Event.cs
Normal file
4
TwitchEventSub/Types/Event.cs
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
namespace TwitchEventSub.Types
|
||||||
|
{
|
||||||
|
public class Event {}
|
||||||
|
}
|
81
TwitchEventSub/Types/EventSubSubscription/Condition.cs
Normal file
81
TwitchEventSub/Types/EventSubSubscription/Condition.cs
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
using System;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace TwitchEventSub.Types.Conditions
|
||||||
|
{
|
||||||
|
#region object oriented
|
||||||
|
public class Condition
|
||||||
|
{
|
||||||
|
public bool Similar(Condition condition)
|
||||||
|
{
|
||||||
|
return JsonConvert.SerializeObject(this) == JsonConvert.SerializeObject(condition);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract class TargetChannel : Condition
|
||||||
|
{
|
||||||
|
public string broadcaster_user_id { get; set; }
|
||||||
|
}
|
||||||
|
public class TargetExtension : Condition
|
||||||
|
{
|
||||||
|
public string extension_client_id { get; set; }
|
||||||
|
}
|
||||||
|
public class TargetSelf : Condition
|
||||||
|
{
|
||||||
|
public string client_id { get; set; }
|
||||||
|
}
|
||||||
|
public class TargetUser : Condition
|
||||||
|
{
|
||||||
|
public string user_id { get; set; }
|
||||||
|
}
|
||||||
|
public abstract class TargetCustomChannelPointsReward : Condition
|
||||||
|
{
|
||||||
|
public string broadcaster_user_id { get; set; }
|
||||||
|
public string reward_id { get; set; }
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
public class ChannelBan : TargetChannel { }
|
||||||
|
public class ChannelSubscribe : TargetChannel { }
|
||||||
|
public class ChannelSubscriptionEnd : TargetChannel { }
|
||||||
|
public class ChannelSubscriptionGift : TargetChannel { }
|
||||||
|
public class ChannelSubscriptionMessage : TargetChannel { }
|
||||||
|
public class ChannelCheer : TargetChannel { }
|
||||||
|
public class ChannelUpdate : TargetChannel { }
|
||||||
|
public class ChannelFollow : TargetChannel { }
|
||||||
|
public class ChannelUnban : TargetChannel { }
|
||||||
|
public class ChannelModeratorAdd : TargetChannel { }
|
||||||
|
public class ChannelModeratorRemove : TargetChannel { }
|
||||||
|
public class ChannelPointsCustomRewardAdd : TargetChannel { }
|
||||||
|
public class ChannelPollBegin : TargetChannel { }
|
||||||
|
public class ChannelPollProgress : TargetChannel { }
|
||||||
|
public class ChannelPollEnd : TargetChannel { }
|
||||||
|
public class ChannelPredictionBegin : TargetChannel { }
|
||||||
|
public class ChannelPredictionProgress : TargetChannel { }
|
||||||
|
public class ChannelPredictionLock : TargetChannel { }
|
||||||
|
public class ChannelPredictionEnd : TargetChannel { }
|
||||||
|
public class Goals : TargetChannel { }
|
||||||
|
public class HypeTrainBegin : TargetChannel { }
|
||||||
|
public class HypeTrainProgress : TargetChannel { }
|
||||||
|
public class HypeTrainEnd : TargetChannel { }
|
||||||
|
public class StreamOnline : TargetChannel { }
|
||||||
|
public class StreamOffline : TargetChannel { }
|
||||||
|
public class ChannelRaid : Condition
|
||||||
|
{
|
||||||
|
public string from_broadcaster_user_id { get; set; }
|
||||||
|
public string to_broadcaster_user_id { get; set; }
|
||||||
|
}
|
||||||
|
public class ChannelPointsCustomRewardUpdate : TargetCustomChannelPointsReward { }
|
||||||
|
public class ChannelPointsCustomRewardRemove : TargetCustomChannelPointsReward { }
|
||||||
|
public class ChannelPointsCustomRewardRedemptionAdd : TargetCustomChannelPointsReward { }
|
||||||
|
public class ChannelPointsCustomRewardRedemptionUpdate : TargetCustomChannelPointsReward { }
|
||||||
|
public class DropEntitlementGrant : Condition
|
||||||
|
{
|
||||||
|
public string organization_id { get; set; }
|
||||||
|
public string category_id { get; set; }
|
||||||
|
public string campaign_id { get; set; }
|
||||||
|
}
|
||||||
|
public class ExtensionBitsTransactionCreate : TargetExtension { }
|
||||||
|
public class UserAuthorizationGrant : TargetSelf { }
|
||||||
|
public class UserAuthorizationRevoke : TargetSelf { }
|
||||||
|
public class UserUpdate : TargetUser { }
|
||||||
|
}
|
23
TwitchEventSub/Types/EventSubSubscription/Request.cs
Normal file
23
TwitchEventSub/Types/EventSubSubscription/Request.cs
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace TwitchEventSub.Types.EventSubSubscription
|
||||||
|
{
|
||||||
|
public class Request
|
||||||
|
{
|
||||||
|
///<summary>
|
||||||
|
///use one of SubscribableTypes
|
||||||
|
///</summary>
|
||||||
|
public string type { get; set; }
|
||||||
|
public string version { get; set; }
|
||||||
|
public Conditions.Condition condition { get; set; }
|
||||||
|
public Transport transport { get; set; }
|
||||||
|
}
|
||||||
|
public class Transport
|
||||||
|
{
|
||||||
|
public TransportMethod method { get; set; } //The transport method. Supported values: webhook.
|
||||||
|
public Uri callback { get; set; } //The callback URL where the notification should be sent. Must use https. Must use port 443.
|
||||||
|
public string secret { get; set; } //The secret used for verifying a signature.
|
||||||
|
}
|
||||||
|
public enum TransportMethod { webhook }
|
||||||
|
}
|
8
TwitchEventSub/Types/EventSubSubscription/Response.cs
Normal file
8
TwitchEventSub/Types/EventSubSubscription/Response.cs
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
namespace TwitchEventSub.Types.EventSubSubscription
|
||||||
|
{
|
||||||
|
public class Response
|
||||||
|
{
|
||||||
|
public Subscription subscription { get; set; }
|
||||||
|
public Event Event { get; set; }
|
||||||
|
}
|
||||||
|
}
|
25
TwitchEventSub/Types/EventSubSubscription/Subscription.cs
Normal file
25
TwitchEventSub/Types/EventSubSubscription/Subscription.cs
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace TwitchEventSub.Types.EventSubSubscription
|
||||||
|
{
|
||||||
|
public class Subscription
|
||||||
|
{
|
||||||
|
public string id { get; set; } //Your client ID.
|
||||||
|
public string type { get; set; } //The notification’s subscription type.
|
||||||
|
public string version { get; set; } //The version of the subscription.
|
||||||
|
public string status { get; set; } //The status of the subscription.
|
||||||
|
public int cost { get; set; } //How much the subscription counts against your limit. See Subscription Limits for more information.
|
||||||
|
public Conditions.Condition condition { get; set; } //Subscription-specific parameters.
|
||||||
|
public string created_at { get; set; } //The tChannelPointsime the notification was created.
|
||||||
|
}
|
||||||
|
internal class UnrefinedSubscription
|
||||||
|
{
|
||||||
|
public string id { get; set; }
|
||||||
|
public string type { get; set; }
|
||||||
|
public string version { get; set; }
|
||||||
|
public string status { get; set; }
|
||||||
|
public int cost { get; set; }
|
||||||
|
public string condition { get; set; }
|
||||||
|
public string created_at { get; set; }
|
||||||
|
}
|
||||||
|
}
|
12
TwitchEventSub/Types/EventSubSubscription/TwitchResponse.cs
Normal file
12
TwitchEventSub/Types/EventSubSubscription/TwitchResponse.cs
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace TwitchEventSub.Types.EventSubSubscription
|
||||||
|
{
|
||||||
|
public class TwitchResponse
|
||||||
|
{
|
||||||
|
public Subscription[] data { get; set; }
|
||||||
|
public int total { get; set; }
|
||||||
|
public int max_total_cost { get; set; }
|
||||||
|
public int total_cost { get; set; }
|
||||||
|
}
|
||||||
|
}
|
15
TwitchEventSub/Types/EventSubSubscription/Twitchogram.cs
Normal file
15
TwitchEventSub/Types/EventSubSubscription/Twitchogram.cs
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
using System.Linq;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace TwitchEventSub.Types.EventSubSubscription
|
||||||
|
{
|
||||||
|
///<summary>
|
||||||
|
/// don't deserialize me, go to SubscribableTypesTranslation and have it "refine" one for you
|
||||||
|
///</summary>
|
||||||
|
public class Twitchogram
|
||||||
|
{
|
||||||
|
public string challenge { get; set; }
|
||||||
|
public Subscription subscription { get; set; }
|
||||||
|
public Event Event { get; set; }
|
||||||
|
}
|
||||||
|
}
|
328
TwitchEventSub/Types/EventSubSubscription/TypeTranslation.cs
Normal file
328
TwitchEventSub/Types/EventSubSubscription/TypeTranslation.cs
Normal file
@ -0,0 +1,328 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace TwitchEventSub.Types.EventSubSubscription
|
||||||
|
{
|
||||||
|
public enum SubscribableTypes
|
||||||
|
{
|
||||||
|
channel_update, //aka Channel Update, version 1. A broadcaster updates their channel properties e.g., category, title, mature flag, broadcast, or language.
|
||||||
|
channel_follow, //aka Channel Follow, version 1. A specified channel receives a follow.
|
||||||
|
channel_subscribe, //aka Channel Subscribe, version 1. A notification when a specified channel receives a subscriber. This does not include resubscribes.
|
||||||
|
channel_subscription_end, //aka Channel Subscription End, version 1. A notification when a subscription to the specified channel ends.
|
||||||
|
channel_subscription_gift, //aka Channel Subscription Gift, version 1. A notification when a viewer gives a gift subscription to one or more users in the specified channel.
|
||||||
|
channel_subscription_message, //aka Channel Subscription Message, version 1. A notification when a user sends a resubscription chat message in a specific channel.
|
||||||
|
channel_cheer, //aka Channel Cheer, version 1. A user cheers on the specified channel.
|
||||||
|
channel_raid, //aka Channel Raid, version 1. A broadcaster raids another broadcaster’s channel.
|
||||||
|
channel_ban, //aka Channel Ban, version 1. A viewer is banned from the specified channel.
|
||||||
|
channel_unban, //aka Channel Unban, version 1. A viewer is unbanned from the specified channel.
|
||||||
|
channel_moderator_add, //aka Channel Moderator Add, version 1. Moderator privileges were added to a user on a specified channel.
|
||||||
|
channel_moderator_remove, //aka Channel Moderator Remove, version 1. Moderator privileges were removed from a user on a specified channel.
|
||||||
|
channel_channel_points_custom_reward_add, //aka Channel Points Custom Reward Add, version 1. A custom channel points reward has been created for the specified channel.
|
||||||
|
channel_channel_points_custom_reward_update, //aka Channel Points Custom Reward Update, version 1. A custom channel points reward has been updated for the specified channel.
|
||||||
|
channel_channel_points_custom_reward_remove, //aka Channel Points Custom Reward Remove, version 1. A custom channel points reward has been removed from the specified channel.
|
||||||
|
channel_channel_points_custom_reward_redemption_add, //aka Channel Points Custom Reward Redemption Add, version 1. A viewer has redeemed a custom channel points reward on the specified channel.
|
||||||
|
channel_channel_points_custom_reward_redemption_update, //aka Channel Points Custom Reward Redemption Update, version 1. A redemption of a channel points custom reward has been updated for the specified channel.
|
||||||
|
channel_poll_begin, //aka Channel Poll Begin, version 1. A poll started on a specified channel.
|
||||||
|
channel_poll_progress, //aka Channel Poll Progress, version 1. Users respond to a poll on a specified channel.
|
||||||
|
channel_poll_end, //aka Channel Poll End, version 1. A poll ended on a specified channel.
|
||||||
|
channel_prediction_begin, //aka Channel Prediction Begin, version 1. A Prediction started on a specified channel.
|
||||||
|
channel_prediction_progress, //aka Channel Prediction Progress, version 1. Users participated in a Prediction on a specified channel.
|
||||||
|
channel_prediction_lock, //aka Channel Prediction Lock, version 1. A Prediction was locked on a specified channel.
|
||||||
|
channel_prediction_end, //aka Channel Prediction End, version 1. A Prediction ended on a specified channel.
|
||||||
|
drop_entitlement_grant, //aka Drop Entitlement Grant, version 1. An entitlement for a Drop is granted to a user.
|
||||||
|
extension_bits_transaction_create, //aka Extension Bits Transaction Create, version 1. A Bits transaction occurred for a specified Twitch Extension.
|
||||||
|
channel_goal_begin, //aka Goal Begin, version 1. Get notified when a broadcaster begins a goal.
|
||||||
|
channel_goal_progress, //aka Goal Progress, version 1. Get notified when progress (either positive or negative) is made towards a broadcaster’s goal.
|
||||||
|
channel_goal_end, //aka Goal End, version 1. Get notified when a broadcaster ends a goal.
|
||||||
|
channel_hype_train_begin, //aka Hype Train Begin, version 1. A Hype Train begins on the specified channel.
|
||||||
|
channel_hype_train_progress, //aka Hype Train Progress, version 1. A Hype Train makes progress on the specified channel.
|
||||||
|
channel_hype_train_end, //aka Hype Train End, version 1. A Hype Train ends on the specified channel.
|
||||||
|
stream_online, //aka Stream Online, version 1. The specified broadcaster starts a stream.
|
||||||
|
stream_offline, //aka Stream Offline, version 1. The specified broadcaster stops a stream.
|
||||||
|
user_authorization_grant, //aka User Authorization Grant, version 1. A user’s authorization has been granted to your client id.
|
||||||
|
user_authorization_revoke, //aka User Authorization Revoke, version 1. A user’s authorization has been revoked for your client id.
|
||||||
|
user_update //aka User Update, version 1. A user has updated their account.
|
||||||
|
}
|
||||||
|
internal class SubscribableTypesTranslation
|
||||||
|
{
|
||||||
|
public static string Enum2String(SubscribableTypes enumedType)
|
||||||
|
{
|
||||||
|
return table.Keys.FirstOrDefault(k => table[k] == enumedType);
|
||||||
|
}
|
||||||
|
public static SubscribableTypes String2Enum(string stringedType)
|
||||||
|
{
|
||||||
|
return table[stringedType];
|
||||||
|
}
|
||||||
|
public static Twitchogram RefineTwitchogram(string raw)
|
||||||
|
{
|
||||||
|
var urT = JsonConvert.DeserializeObject<Twitchogram>(raw);
|
||||||
|
var refinedSubscription = new Subscription()
|
||||||
|
{
|
||||||
|
id = urT.subscription.id,
|
||||||
|
type = urT.subscription.type,
|
||||||
|
version = urT.subscription.version,
|
||||||
|
status = urT.subscription.status,
|
||||||
|
cost = urT.subscription.cost,
|
||||||
|
created_at = urT.subscription.created_at
|
||||||
|
};
|
||||||
|
|
||||||
|
var refinedTuple = TypedPieces(urT.subscription.type);
|
||||||
|
|
||||||
|
refinedSubscription.condition = refinedTuple.Item1;
|
||||||
|
|
||||||
|
var refinedTwitchogram = new Twitchogram()
|
||||||
|
{
|
||||||
|
challenge = urT.challenge,
|
||||||
|
subscription = refinedSubscription,
|
||||||
|
Event = refinedTuple.Item2
|
||||||
|
};
|
||||||
|
JsonConvert.PopulateObject(raw, refinedTwitchogram);
|
||||||
|
return refinedTwitchogram;
|
||||||
|
}
|
||||||
|
public static TwitchResponse RefineTwitchResponse(string raw)
|
||||||
|
{
|
||||||
|
var refinedResponse = JsonConvert.DeserializeObject<TwitchResponse>(raw);
|
||||||
|
foreach(var d in refinedResponse.data)
|
||||||
|
{
|
||||||
|
d.condition = TypedCondition(d.type);
|
||||||
|
}
|
||||||
|
JsonConvert.PopulateObject(raw, refinedResponse);
|
||||||
|
|
||||||
|
return refinedResponse;
|
||||||
|
}
|
||||||
|
public static Event TypedEvent(string stringType)
|
||||||
|
{
|
||||||
|
return TypedPieces(stringType).Item2;
|
||||||
|
}
|
||||||
|
public static Conditions.Condition TypedCondition(string stringType)
|
||||||
|
{
|
||||||
|
return TypedPieces(stringType).Item1;
|
||||||
|
}
|
||||||
|
public static Tuple<Conditions.Condition, Event> TypedPieces(string stringType)
|
||||||
|
{
|
||||||
|
switch (String2Enum(stringType))
|
||||||
|
{
|
||||||
|
case SubscribableTypes.channel_update:
|
||||||
|
return new Tuple<Conditions.Condition, Event>(
|
||||||
|
new Conditions.ChannelUpdate(),
|
||||||
|
new Types.Offline.ChannelUpdate());
|
||||||
|
case SubscribableTypes.channel_follow:
|
||||||
|
return new Tuple<Conditions.Condition, Event>(
|
||||||
|
new Conditions.ChannelFollow(),
|
||||||
|
new Types.Offline.ChannelFollow());
|
||||||
|
|
||||||
|
case SubscribableTypes.channel_subscribe:
|
||||||
|
return new Tuple<Conditions.Condition, Event>(
|
||||||
|
new Conditions.ChannelSubscribe(),
|
||||||
|
new Types.ChannelSubscription.ChannelSubscribe());
|
||||||
|
|
||||||
|
case SubscribableTypes.channel_subscription_end:
|
||||||
|
return new Tuple<Conditions.Condition, Event>(
|
||||||
|
new Conditions.ChannelSubscriptionEnd(),
|
||||||
|
new Types.ChannelSubscription.ChannelSubscriptionEnd());
|
||||||
|
|
||||||
|
case SubscribableTypes.channel_subscription_gift:
|
||||||
|
return new Tuple<Conditions.Condition, Event>(
|
||||||
|
new Conditions.ChannelSubscriptionGift(),
|
||||||
|
new Types.ChannelSubscription.ChannelSubscriptionGift());
|
||||||
|
|
||||||
|
case SubscribableTypes.channel_subscription_message:
|
||||||
|
return new Tuple<Conditions.Condition, Event>(
|
||||||
|
new Conditions.ChannelSubscriptionMessage(),
|
||||||
|
new Types.ChannelSubscription.ChannelSubscriptionMessage());
|
||||||
|
|
||||||
|
case SubscribableTypes.channel_cheer:
|
||||||
|
return new Tuple<Conditions.Condition, Event>(
|
||||||
|
new Conditions.ChannelCheer(),
|
||||||
|
new Types.ChannelCheer());
|
||||||
|
|
||||||
|
case SubscribableTypes.channel_raid:
|
||||||
|
return new Tuple<Conditions.Condition, Event>(
|
||||||
|
new Conditions.ChannelRaid(),
|
||||||
|
new Types.ChannelRaid());
|
||||||
|
|
||||||
|
case SubscribableTypes.channel_ban:
|
||||||
|
return new Tuple<Conditions.Condition, Event>(
|
||||||
|
new Conditions.ChannelBan(),
|
||||||
|
new Types.Moderation.ChannelBan());
|
||||||
|
|
||||||
|
case SubscribableTypes.channel_unban:
|
||||||
|
return new Tuple<Conditions.Condition, Event>(
|
||||||
|
new Conditions.ChannelUnban(),
|
||||||
|
new Types.Moderation.ChannelUnban());
|
||||||
|
|
||||||
|
case SubscribableTypes.channel_moderator_add:
|
||||||
|
return new Tuple<Conditions.Condition, Event>(
|
||||||
|
new Conditions.ChannelModeratorAdd(),
|
||||||
|
new Types.Moderation.ChannelModeratorAdd());
|
||||||
|
|
||||||
|
case SubscribableTypes.channel_moderator_remove:
|
||||||
|
return new Tuple<Conditions.Condition, Event>(
|
||||||
|
new Conditions.ChannelModeratorRemove(),
|
||||||
|
new Types.Moderation.ChannelModeratorRemove());
|
||||||
|
|
||||||
|
case SubscribableTypes.channel_channel_points_custom_reward_add:
|
||||||
|
return new Tuple<Conditions.Condition, Event>(
|
||||||
|
new Conditions.ChannelPointsCustomRewardAdd(),
|
||||||
|
new Types.ChannelPoints.ChannelPointsCustomRewardAdd());
|
||||||
|
|
||||||
|
case SubscribableTypes.channel_channel_points_custom_reward_update:
|
||||||
|
return new Tuple<Conditions.Condition, Event>(
|
||||||
|
new Conditions.ChannelPointsCustomRewardUpdate(),
|
||||||
|
new Types.ChannelPoints.ChannelPointsCustomRewardUpdate());
|
||||||
|
|
||||||
|
case SubscribableTypes.channel_channel_points_custom_reward_remove:
|
||||||
|
return new Tuple<Conditions.Condition, Event>(
|
||||||
|
new Conditions.ChannelPointsCustomRewardRemove(),
|
||||||
|
new Types.ChannelPoints.ChannelPointsCustomRewardRemove());
|
||||||
|
|
||||||
|
case SubscribableTypes.channel_channel_points_custom_reward_redemption_add:
|
||||||
|
return new Tuple<Conditions.Condition, Event>(
|
||||||
|
new Conditions.ChannelPointsCustomRewardRedemptionAdd(),
|
||||||
|
new Types.ChannelPoints.ChannelPointsCustomRewardRedemptionAdd());
|
||||||
|
|
||||||
|
case SubscribableTypes.channel_channel_points_custom_reward_redemption_update:
|
||||||
|
return new Tuple<Conditions.Condition, Event>(
|
||||||
|
new Conditions.ChannelPointsCustomRewardRedemptionUpdate(),
|
||||||
|
new Types.ChannelPoints.ChannelPointsCustomRewardRedemptionUpdate());
|
||||||
|
|
||||||
|
case SubscribableTypes.channel_poll_begin:
|
||||||
|
return new Tuple<Conditions.Condition, Event>(
|
||||||
|
new Conditions.ChannelPollBegin(),
|
||||||
|
new Types.Poll.ChannelPollBegin());
|
||||||
|
|
||||||
|
case SubscribableTypes.channel_poll_progress:
|
||||||
|
return new Tuple<Conditions.Condition, Event>(
|
||||||
|
new Conditions.ChannelPollProgress(),
|
||||||
|
new Types.Poll.ChannelPollProgress());
|
||||||
|
|
||||||
|
case SubscribableTypes.channel_poll_end:
|
||||||
|
return new Tuple<Conditions.Condition, Event>(
|
||||||
|
new Conditions.ChannelPollEnd(),
|
||||||
|
new Types.Poll.ChannelPollEnd());
|
||||||
|
|
||||||
|
case SubscribableTypes.channel_prediction_begin:
|
||||||
|
return new Tuple<Conditions.Condition, Event>(
|
||||||
|
new Conditions.ChannelPollBegin(),
|
||||||
|
new Types.Poll.ChannelPollBegin());
|
||||||
|
|
||||||
|
case SubscribableTypes.channel_prediction_progress:
|
||||||
|
return new Tuple<Conditions.Condition, Event>(
|
||||||
|
new Conditions.ChannelPollProgress(),
|
||||||
|
new Types.Poll.ChannelPollProgress());
|
||||||
|
|
||||||
|
case SubscribableTypes.channel_prediction_lock:
|
||||||
|
return new Tuple<Conditions.Condition, Event>(
|
||||||
|
new Conditions.ChannelPredictionLock(),
|
||||||
|
new Types.Prediction.ChannelPredictionLock());
|
||||||
|
|
||||||
|
case SubscribableTypes.channel_prediction_end:
|
||||||
|
return new Tuple<Conditions.Condition, Event>(
|
||||||
|
new Conditions.ChannelPredictionEnd(),
|
||||||
|
new Types.Prediction.ChannelPredictionEnd());
|
||||||
|
|
||||||
|
case SubscribableTypes.drop_entitlement_grant:
|
||||||
|
return new Tuple<Conditions.Condition, Event>(
|
||||||
|
new Conditions.DropEntitlementGrant(),
|
||||||
|
new Types.DropEntitlementGrant());
|
||||||
|
|
||||||
|
case SubscribableTypes.extension_bits_transaction_create:
|
||||||
|
return new Tuple<Conditions.Condition, Event>(
|
||||||
|
new Conditions.ExtensionBitsTransactionCreate(),
|
||||||
|
new Types.ExtensionBitsTransactionCreate());
|
||||||
|
|
||||||
|
case SubscribableTypes.channel_goal_begin:
|
||||||
|
case SubscribableTypes.channel_goal_progress:
|
||||||
|
case SubscribableTypes.channel_goal_end:
|
||||||
|
return new Tuple<Conditions.Condition, Event>(
|
||||||
|
new Conditions.Goals(),
|
||||||
|
new Types.Goals());
|
||||||
|
|
||||||
|
case SubscribableTypes.channel_hype_train_begin:
|
||||||
|
return new Tuple<Conditions.Condition, Event>(
|
||||||
|
new Conditions.HypeTrainBegin(),
|
||||||
|
new Types.HypeTrain.HypeTrainBegin());
|
||||||
|
|
||||||
|
case SubscribableTypes.channel_hype_train_progress:
|
||||||
|
return new Tuple<Conditions.Condition, Event>(
|
||||||
|
new Conditions.HypeTrainProgress(),
|
||||||
|
new Types.HypeTrain.HypeTrainProgress());
|
||||||
|
|
||||||
|
case SubscribableTypes.channel_hype_train_end:
|
||||||
|
return new Tuple<Conditions.Condition, Event>(
|
||||||
|
new Conditions.HypeTrainEnd(),
|
||||||
|
new Types.HypeTrain.HypeTrainEnd());
|
||||||
|
|
||||||
|
case SubscribableTypes.stream_online:
|
||||||
|
return new Tuple<Conditions.Condition, Event>(
|
||||||
|
new Conditions.StreamOnline(),
|
||||||
|
new Types.StreamOnline());
|
||||||
|
|
||||||
|
case SubscribableTypes.stream_offline:
|
||||||
|
return new Tuple<Conditions.Condition, Event>(
|
||||||
|
new Conditions.StreamOffline(),
|
||||||
|
new Types.StreamOffline());
|
||||||
|
|
||||||
|
case SubscribableTypes.user_authorization_grant:
|
||||||
|
return new Tuple<Conditions.Condition, Event>(
|
||||||
|
new Conditions.UserAuthorizationGrant(),
|
||||||
|
new Types.Offline.UserAuthorizationGrant());
|
||||||
|
|
||||||
|
case SubscribableTypes.user_authorization_revoke:
|
||||||
|
return new Tuple<Conditions.Condition, Event>(
|
||||||
|
new Conditions.UserAuthorizationRevoke(),
|
||||||
|
new Types.Offline.UserAuthorizationRevoke());
|
||||||
|
|
||||||
|
case SubscribableTypes.user_update:
|
||||||
|
return new Tuple<Conditions.Condition, Event>(
|
||||||
|
new Conditions.UserUpdate(),
|
||||||
|
new Types.Offline.UserUpdate());
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new ArgumentOutOfRangeException($"I've never heard of {stringType}. That's not real. I don't believe in that.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private static readonly Dictionary<string, SubscribableTypes> table = new Dictionary<string, SubscribableTypes>()
|
||||||
|
{
|
||||||
|
{"channel.update", SubscribableTypes.channel_update},
|
||||||
|
{"channel.follow", SubscribableTypes.channel_follow},
|
||||||
|
{"channel.subscribe", SubscribableTypes.channel_subscribe},
|
||||||
|
{"channel.subscription.end", SubscribableTypes.channel_subscription_end},
|
||||||
|
{"channel.subscription.gift", SubscribableTypes.channel_subscription_gift},
|
||||||
|
{"channel.subscription.message", SubscribableTypes.channel_subscription_message},
|
||||||
|
{"channel.cheer", SubscribableTypes.channel_cheer},
|
||||||
|
{"channel.raid", SubscribableTypes.channel_raid},
|
||||||
|
{"channel.ban", SubscribableTypes.channel_ban},
|
||||||
|
{"channel.unban", SubscribableTypes.channel_unban},
|
||||||
|
{"channel.moderator.add", SubscribableTypes.channel_moderator_add},
|
||||||
|
{"channel.moderator.remove", SubscribableTypes.channel_moderator_remove},
|
||||||
|
{"channel.channel.points.custom.reward.add", SubscribableTypes.channel_channel_points_custom_reward_add},
|
||||||
|
{"channel.channel.points.custom.reward.update", SubscribableTypes.channel_channel_points_custom_reward_update},
|
||||||
|
{"channel.channel.points.custom.reward.remove", SubscribableTypes.channel_channel_points_custom_reward_remove},
|
||||||
|
{"channel.channel.points.custom.reward.redemption.add", SubscribableTypes.channel_channel_points_custom_reward_redemption_add},
|
||||||
|
{"channel.channel.points.custom.reward.redemption.update", SubscribableTypes.channel_channel_points_custom_reward_redemption_update},
|
||||||
|
{"channel.poll.begin", SubscribableTypes.channel_poll_begin},
|
||||||
|
{"channel.poll.progress", SubscribableTypes.channel_poll_progress},
|
||||||
|
{"channel.poll.end", SubscribableTypes.channel_poll_end},
|
||||||
|
{"channel.prediction.begin", SubscribableTypes.channel_prediction_begin},
|
||||||
|
{"channel.prediction.progress", SubscribableTypes.channel_prediction_progress},
|
||||||
|
{"channel.prediction.lock", SubscribableTypes.channel_prediction_lock},
|
||||||
|
{"channel.prediction.end", SubscribableTypes.channel_prediction_end},
|
||||||
|
{"drop.entitlement.grant", SubscribableTypes.drop_entitlement_grant},
|
||||||
|
{"extension.bits.transaction.create", SubscribableTypes.extension_bits_transaction_create},
|
||||||
|
{"channel.goal.begin", SubscribableTypes.channel_goal_begin},
|
||||||
|
{"channel.goal.progress", SubscribableTypes.channel_goal_progress},
|
||||||
|
{"channel.goal.end", SubscribableTypes.channel_goal_end},
|
||||||
|
{"channel.hype.train.begin", SubscribableTypes.channel_hype_train_begin},
|
||||||
|
{"channel.hype.train.progress", SubscribableTypes.channel_hype_train_progress},
|
||||||
|
{"channel.hype.train.end", SubscribableTypes.channel_hype_train_end},
|
||||||
|
{"stream.online", SubscribableTypes.stream_online},
|
||||||
|
{"stream.offline", SubscribableTypes.stream_offline},
|
||||||
|
{"user.authorization.grant", SubscribableTypes.user_authorization_grant},
|
||||||
|
{"user.authorization.revoke", SubscribableTypes.user_authorization_revoke},
|
||||||
|
{"user.update", SubscribableTypes.user_update}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
23
TwitchEventSub/Types/ExtensionBitsTransaction.cs
Normal file
23
TwitchEventSub/Types/ExtensionBitsTransaction.cs
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
namespace TwitchEventSub.Types
|
||||||
|
{
|
||||||
|
public class ExtensionBitsTransactionCreate : Event
|
||||||
|
{
|
||||||
|
public string extension_client_id { get; set; } //Client ID of the extension.
|
||||||
|
public string id { get; set; } //Transaction ID.
|
||||||
|
public string broadcaster_user_id { get; set; } //The transaction’s broadcaster ID.
|
||||||
|
public string broadcaster_user_login { get; set; } //The transaction’s broadcaster login.
|
||||||
|
public string broadcaster_user_name { get; set; } //The transaction’s broadcaster display name.
|
||||||
|
public string user_id { get; set; } //The transaction’s user ID.
|
||||||
|
public string user_login { get; set; } //The transaction’s user login.
|
||||||
|
public string user_name { get; set; } //The transaction’s user display name.
|
||||||
|
public ExtensionProduct product { get; set; } //Additional extension product information.
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ExtensionProduct
|
||||||
|
{
|
||||||
|
public string name { get; set; }
|
||||||
|
public int bits { get; set; } //involved in the transaction
|
||||||
|
public string sku { get; set; } //Unique identifier for the product acquired.
|
||||||
|
public bool in_development { get; set; } //Flag indicating if the product is in development. If in_development is true, bits will be 0.
|
||||||
|
}
|
||||||
|
}
|
18
TwitchEventSub/Types/Goals.cs
Normal file
18
TwitchEventSub/Types/Goals.cs
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
namespace TwitchEventSub.Types
|
||||||
|
{
|
||||||
|
public class Goals : Event
|
||||||
|
{
|
||||||
|
public string id { get; set; } //An ID that identifies this event.
|
||||||
|
public string broadcaster_user_id { get; set; } //An ID that uniquely identifies the broadcaster.
|
||||||
|
public string broadcaster_user_name { get; set; } //The broadcaster’s display name.
|
||||||
|
public string broadcaster_user_login { get; set; } //The broadcaster’s user handle.
|
||||||
|
public GoalType type { get; set; } //The type of goal. Possible values are: followers, subscriptions
|
||||||
|
public string description { get; set; } //maximum of 40 characters
|
||||||
|
public bool? is_achieved { get; set; } //whether the broadcaster achieved their goal. Only the channel.goal.end event includes this
|
||||||
|
public int current_amount { get; set; }
|
||||||
|
public int target_amount { get; set; }
|
||||||
|
public string started_at { get; set; } //The UTC timestamp in RFC 3339 format, which indicates when the broadcaster created the goal.
|
||||||
|
public string ended_at { get; set; } //The UTC timestamp in RFC 3339 format, which indicates when the broadcaster ended the goal. Only the channel.goal.end event includes this field.
|
||||||
|
}
|
||||||
|
public enum GoalType { followers, subscriptions }
|
||||||
|
}
|
12
TwitchEventSub/Types/HypeTrain/Contribution.cs
Normal file
12
TwitchEventSub/Types/HypeTrain/Contribution.cs
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
namespace TwitchEventSub.Types.HypeTrain
|
||||||
|
{
|
||||||
|
public class Contribution
|
||||||
|
{
|
||||||
|
public string user_id { get; set; } //The ID of the user.
|
||||||
|
public string user_login { get; set; } //The login of the user.
|
||||||
|
public string user_name { get; set; } //The display name of the user.
|
||||||
|
public ContributionType type { get; set; } //Type of contribution. Valid values include bits, subscription.
|
||||||
|
public int total { get; set; }
|
||||||
|
}
|
||||||
|
public enum ContributionType { unknown, bits, subscription }
|
||||||
|
}
|
32
TwitchEventSub/Types/HypeTrain/HypeTrain.cs
Normal file
32
TwitchEventSub/Types/HypeTrain/HypeTrain.cs
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
namespace TwitchEventSub.Types.HypeTrain
|
||||||
|
{
|
||||||
|
public abstract class HypeTrainEvents : Event
|
||||||
|
{
|
||||||
|
public string id { get; set; } //The Hype Train ID.
|
||||||
|
public string broadcaster_user_id { get; set; } //The requested broadcaster ID.
|
||||||
|
public string broadcaster_user_login { get; set; } //The requested broadcaster login.
|
||||||
|
public string broadcaster_user_name { get; set; } //The requested broadcaster display name.
|
||||||
|
public int level { get; set; } //The current level of the Hype Train.
|
||||||
|
public int total { get; set; } //Total points contributed to the Hype Train.
|
||||||
|
public Contribution top_contributions { get; set; } //The contributors with the most points contributed.
|
||||||
|
public string started_at { get; set; } //The time when the Hype Train started.
|
||||||
|
}
|
||||||
|
|
||||||
|
public class HypeTrainProgress: HypeTrainEvents
|
||||||
|
{
|
||||||
|
|
||||||
|
public int progress { get; set; } //The number of points contributed to the Hype Train at the current level.
|
||||||
|
public int goal { get; set; } //The number of points required to reach the next level.
|
||||||
|
public Contribution last_contribution { get; set; } //The most recent contribution.
|
||||||
|
public string expires_at { get; set; } //The time when the Hype Train expires. The expiration is extended when the Hype Train reaches a new level.
|
||||||
|
}
|
||||||
|
public class HypeTrainBegin : HypeTrainProgress
|
||||||
|
{
|
||||||
|
new private int level{get;set;} = 1;
|
||||||
|
}
|
||||||
|
public class HypeTrainEnd : HypeTrainEvents
|
||||||
|
{
|
||||||
|
public string ended_at { get; set; } //The time when the Hype Train ended.
|
||||||
|
public string cooldown_ends_at { get; set; } //The time when the Hype Train cooldown ends so that the next Hype Train can start.
|
||||||
|
}
|
||||||
|
}
|
19
TwitchEventSub/Types/Moderation/ChannelBan.cs
Normal file
19
TwitchEventSub/Types/Moderation/ChannelBan.cs
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
namespace TwitchEventSub.Types.Moderation
|
||||||
|
{
|
||||||
|
public class ChannelBan : Event
|
||||||
|
{
|
||||||
|
public string user_id { get; set; }
|
||||||
|
public string user_login { get; set; }
|
||||||
|
public string user_name { get; set; }
|
||||||
|
public string broadcaster_user_id { get; set; }
|
||||||
|
public string broadcaster_user_login { get; set; }
|
||||||
|
public string broadcaster_user_name { get; set; }
|
||||||
|
public string moderator_user_id { get; set; }
|
||||||
|
public string moderator_user_login { get; set; }
|
||||||
|
public string moderator_user_name { get; set; }
|
||||||
|
public string reason { get; set; }
|
||||||
|
public string ends_at { get; set; }
|
||||||
|
public bool is_permanent { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
12
TwitchEventSub/Types/Moderation/ChannelModeratorAdd.cs
Normal file
12
TwitchEventSub/Types/Moderation/ChannelModeratorAdd.cs
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
namespace TwitchEventSub.Types.Moderation
|
||||||
|
{
|
||||||
|
public class ChannelModeratorAdd : Event
|
||||||
|
{
|
||||||
|
public string broadcaster_user_id { get; set; } //The requested broadcaster ID.
|
||||||
|
public string broadcaster_user_login { get; set; } //The requested broadcaster login.
|
||||||
|
public string broadcaster_user_name { get; set; } //The requested broadcaster display name.
|
||||||
|
public string user_id { get; set; } //The user ID of the new moderator.
|
||||||
|
public string user_login { get; set; } //The user login of the new moderator.
|
||||||
|
public string user_name { get; set; } //The display name of the new moderator.
|
||||||
|
}
|
||||||
|
}
|
12
TwitchEventSub/Types/Moderation/ChannelModeratorRemove.cs
Normal file
12
TwitchEventSub/Types/Moderation/ChannelModeratorRemove.cs
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
namespace TwitchEventSub.Types.Moderation
|
||||||
|
{
|
||||||
|
public class ChannelModeratorRemove : Event
|
||||||
|
{
|
||||||
|
public string broadcaster_user_id { get; set; } //The requested broadcaster ID.
|
||||||
|
public string broadcaster_user_login { get; set; } //The requested broadcaster login.
|
||||||
|
public string broadcaster_user_name { get; set; } //The requested broadcaster display name.
|
||||||
|
public string user_id { get; set; } //The user ID of the removed moderator.
|
||||||
|
public string user_login { get; set; } //The user login of the removed moderator.
|
||||||
|
public string user_name { get; set; } //The display name of the removed moderator.
|
||||||
|
}
|
||||||
|
}
|
16
TwitchEventSub/Types/Moderation/ChannelUnban.cs
Normal file
16
TwitchEventSub/Types/Moderation/ChannelUnban.cs
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
namespace TwitchEventSub.Types.Moderation
|
||||||
|
{
|
||||||
|
|
||||||
|
public class ChannelUnban : Event
|
||||||
|
{
|
||||||
|
public string user_id { get; set; } //The user id for the user who was unbanned on the specified channel.
|
||||||
|
public string user_login { get; set; } //The user login for the user who was unbanned on the specified channel.
|
||||||
|
public string user_name { get; set; } //The user display name for the user who was unbanned on the specified channel.
|
||||||
|
public string broadcaster_user_id { get; set; } //The requested broadcaster ID.
|
||||||
|
public string broadcaster_user_login { get; set; } //The requested broadcaster login.
|
||||||
|
public string broadcaster_user_name { get; set; } //The requested broadcaster display name.
|
||||||
|
public string moderator_user_id { get; set; } //The user ID of the issuer of the unban.
|
||||||
|
public string moderator_user_login { get; set; } //The user login of the issuer of the unban.
|
||||||
|
public string moderator_user_name { get; set; } //The user name of the issuer of the unban.
|
||||||
|
}
|
||||||
|
}
|
13
TwitchEventSub/Types/Offline/ChannelFollow.cs
Normal file
13
TwitchEventSub/Types/Offline/ChannelFollow.cs
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
namespace TwitchEventSub.Types.Offline
|
||||||
|
{
|
||||||
|
public class ChannelFollow : Event
|
||||||
|
{
|
||||||
|
public string user_id { get; set; } //The user ID for the user now following the specified channel.
|
||||||
|
public string user_login { get; set; } //The user login for the user now following the specified channel.
|
||||||
|
public string user_name { get; set; } //The user display name for the user now following the specified channel.
|
||||||
|
public string broadcaster_user_id { get; set; } //The requested broadcaster ID.
|
||||||
|
public string broadcaster_user_login { get; set; } //The requested broadcaster login.
|
||||||
|
public string broadcaster_user_name { get; set; } //The requested broadcaster display name.
|
||||||
|
public string followed_at { get; set; } //RFC3339 timestamp of when the follow occurred.
|
||||||
|
}
|
||||||
|
}
|
14
TwitchEventSub/Types/Offline/ChannelUpdate.cs
Normal file
14
TwitchEventSub/Types/Offline/ChannelUpdate.cs
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
namespace TwitchEventSub.Types.Offline
|
||||||
|
{
|
||||||
|
public class ChannelUpdate : Event
|
||||||
|
{
|
||||||
|
public string broadcaster_user_id { get; set; } //The broadcaster’s user ID.
|
||||||
|
public string broadcaster_user_login { get; set; } //The broadcaster’s user login.
|
||||||
|
public string broadcaster_user_name { get; set; } //The broadcaster’s user display name.
|
||||||
|
public string title { get; set; } //The channel’s stream title.
|
||||||
|
public string language { get; set; } //The channel’s broadcast language.
|
||||||
|
public string category_id { get; set; } //The channel’s category ID.
|
||||||
|
public string category_name { get; set; } //The category name.
|
||||||
|
public bool is_mature { get; set; }
|
||||||
|
}
|
||||||
|
}
|
1
TwitchEventSub/Types/Offline/README.txt
Normal file
1
TwitchEventSub/Types/Offline/README.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
stuff that is likely to happen or at least makes sense offline? i can't think of a good name
|
12
TwitchEventSub/Types/Offline/UserAuthorizationUpdate.cs
Normal file
12
TwitchEventSub/Types/Offline/UserAuthorizationUpdate.cs
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
namespace TwitchEventSub.Types.Offline
|
||||||
|
{
|
||||||
|
public class UserAuthorizationUpdate : Event
|
||||||
|
{
|
||||||
|
public string client_id { get; set; } //The client_id of the application that was granted user access.
|
||||||
|
public string user_id { get; set; } //The user id for the user who has granted authorization for your client id.
|
||||||
|
public string user_login { get; set; } //The user login for the user who has granted authorization for your client id.
|
||||||
|
public string user_name { get; set; } //The user display name for the user who has granted authorization for your client id.
|
||||||
|
}
|
||||||
|
public class UserAuthorizationGrant : UserAuthorizationUpdate { }
|
||||||
|
public class UserAuthorizationRevoke : UserAuthorizationUpdate { }
|
||||||
|
}
|
11
TwitchEventSub/Types/Offline/UserUpdate.cs
Normal file
11
TwitchEventSub/Types/Offline/UserUpdate.cs
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
namespace TwitchEventSub.Types.Offline
|
||||||
|
{
|
||||||
|
public class UserUpdate : Event
|
||||||
|
{
|
||||||
|
public string user_id { get; set; }
|
||||||
|
public string user_login { get; set; }
|
||||||
|
public string user_name { get; set; } //The user’s user display name.
|
||||||
|
public string email { get; set; } //The user’s email. Only included if you have the user:read:email scope for the user.
|
||||||
|
public string description { get; set; }
|
||||||
|
}
|
||||||
|
}
|
31
TwitchEventSub/Types/Poll/Poll.cs
Normal file
31
TwitchEventSub/Types/Poll/Poll.cs
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
namespace TwitchEventSub.Types.Poll
|
||||||
|
{
|
||||||
|
public class PollEvent : Event
|
||||||
|
{
|
||||||
|
public string id { get; set; } //ID of the poll.
|
||||||
|
public string broadcaster_user_id { get; set; } //The requested broadcaster ID.
|
||||||
|
public string broadcaster_user_login { get; set; } //The requested broadcaster login.
|
||||||
|
public string broadcaster_user_name { get; set; } //The requested broadcaster display name.
|
||||||
|
public string title { get; set; } //Question displayed for the poll.
|
||||||
|
public PollChoice[] choices { get; set; } //An array of choices for the poll.
|
||||||
|
public VotingMethod bits_voting { get; set; } //The Bits voting settings for the poll.
|
||||||
|
public VotingMethod channel_points_voting { get; set; } //The Channel Points voting settings for the poll.
|
||||||
|
public string started_at { get; set; } //The time the poll started.
|
||||||
|
}
|
||||||
|
public class ChannelPollBegin : PollEvent
|
||||||
|
{
|
||||||
|
public string ends_at { get; set; } //The time the poll will end.
|
||||||
|
}
|
||||||
|
public class ChannelPollProgress : PollEvent
|
||||||
|
{
|
||||||
|
public string ends_at { get; set; } //The time the poll will end.
|
||||||
|
}
|
||||||
|
public class ChannelPollEnd: PollEvent
|
||||||
|
{
|
||||||
|
public ChannelPollStatus status { get; set; } //The status of the poll. Valid values are completed, archived, and terminated.
|
||||||
|
public string ended_at { get; set; } //The time the poll ended.
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum ChannelPollStatus { completed, archived, terminated }
|
||||||
|
|
||||||
|
}
|
11
TwitchEventSub/Types/Poll/PollChoice.cs
Normal file
11
TwitchEventSub/Types/Poll/PollChoice.cs
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
namespace TwitchEventSub.Types.Poll
|
||||||
|
{
|
||||||
|
public class PollChoice
|
||||||
|
{
|
||||||
|
public string id { get; set; }
|
||||||
|
public string title { get; set; } //Text displayed for the choice.
|
||||||
|
public int bits_votes { get; set; } //Number of votes received via Bits.
|
||||||
|
public int channel_points_votes { get; set; } //Number of votes received via Channel Points.
|
||||||
|
public int votes { get; set; } //Total number of votes received for the choice across all methods of voting.
|
||||||
|
}
|
||||||
|
}
|
8
TwitchEventSub/Types/Poll/VotingMethod.cs
Normal file
8
TwitchEventSub/Types/Poll/VotingMethod.cs
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
namespace TwitchEventSub.Types.Poll
|
||||||
|
{
|
||||||
|
public class VotingMethod
|
||||||
|
{
|
||||||
|
public bool is_enabled { get; set; }
|
||||||
|
public int amount_per_vote { get; set; }
|
||||||
|
}
|
||||||
|
}
|
20
TwitchEventSub/Types/Prediction/ChannelPredictionUpdate.cs
Normal file
20
TwitchEventSub/Types/Prediction/ChannelPredictionUpdate.cs
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
namespace TwitchEventSub.Types.Prediction
|
||||||
|
{
|
||||||
|
public class ChannelPredictionProgress : Event
|
||||||
|
{
|
||||||
|
public string id { get; set; } //Channel Points Prediction ID.
|
||||||
|
public string broadcaster_user_id { get; set; } //The requested broadcaster ID.
|
||||||
|
public string broadcaster_user_login { get; set; } //The requested broadcaster login.
|
||||||
|
public string broadcaster_user_name { get; set; } //The requested broadcaster display name.
|
||||||
|
public string title { get; set; } //Title for the Channel Points Prediction.
|
||||||
|
public Outcome[] outcomes { get; set; } //An array of outcomes for the Channel Points Prediction. Includes top_predictors.
|
||||||
|
public string started_at { get; set; } //The time the Channel Points Prediction started.
|
||||||
|
public string locks_at { get; set; } //The time the Channel Points Prediction will automatically lock.
|
||||||
|
}
|
||||||
|
public class ChannelPredictionBegin : ChannelPredictionProgress {}
|
||||||
|
public class ChannelPredictionLock : ChannelPredictionProgress {}
|
||||||
|
public class ChannelPredictionEnd : ChannelPredictionProgress
|
||||||
|
{
|
||||||
|
public string winning_outcome_id { get; set; } //docs say it's a string.
|
||||||
|
}
|
||||||
|
}
|
12
TwitchEventSub/Types/Prediction/Outcome.cs
Normal file
12
TwitchEventSub/Types/Prediction/Outcome.cs
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
namespace TwitchEventSub.Types.Prediction
|
||||||
|
{
|
||||||
|
public class Outcome
|
||||||
|
{
|
||||||
|
public string id { get; set; } //The outcome ID.
|
||||||
|
public string title { get; set; } //The outcome title.
|
||||||
|
public string color { get; set; } //The color for the outcome. Valid values are pink and blue.
|
||||||
|
public int users { get; set; } //The number of users who used Channel Points on this outcome.
|
||||||
|
public int channel_points { get; set; } //The total number of Channel Points used on this outcome.
|
||||||
|
public Predictor[] top_predictors { get; set; } //An array of up to 10 users who used the most Channel Points on this outcome.
|
||||||
|
}
|
||||||
|
}
|
11
TwitchEventSub/Types/Prediction/Predictor.cs
Normal file
11
TwitchEventSub/Types/Prediction/Predictor.cs
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
namespace TwitchEventSub.Types.Prediction
|
||||||
|
{
|
||||||
|
public class Predictor
|
||||||
|
{
|
||||||
|
public string user_id { get; set; } //The ID of the user.
|
||||||
|
public string user_login { get; set; } //The login of the user.
|
||||||
|
public string user_name { get; set; } //The display name of the user.
|
||||||
|
public int channel_points_won { get; set; } //The number of Channel Points won. This value is always null in the event payload for Prediction progress and Prediction lock. This value is 0 if the outcome did not win or if the Prediction was canceled and Channel Points were refunded.
|
||||||
|
public int channel_points_used { get; set; } //The number of Channel Points used to participate in the Prediction.
|
||||||
|
}
|
||||||
|
}
|
17
TwitchEventSub/Types/StreamActivity.cs
Normal file
17
TwitchEventSub/Types/StreamActivity.cs
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
namespace TwitchEventSub.Types
|
||||||
|
{
|
||||||
|
public class StreamActivity : Event
|
||||||
|
{
|
||||||
|
public string broadcaster_user_id { get; set; } //The broadcaster’s user id.
|
||||||
|
public string broadcaster_user_login { get; set; } //The broadcaster’s user login.
|
||||||
|
public string broadcaster_user_name { get; set; } //The broadcaster’s user display name.
|
||||||
|
}
|
||||||
|
public class StreamOffline : StreamActivity {}
|
||||||
|
|
||||||
|
public class StreamOnline : StreamActivity
|
||||||
|
{
|
||||||
|
public string id { get; set; } //The id of the stream.
|
||||||
|
public string type { get; set; } //The stream type. Valid values are: live, playlist, watch_party, premiere, rerun.
|
||||||
|
public string started_at { get; set; } //The timestamp at which the stream went online at.
|
||||||
|
}
|
||||||
|
}
|
8
appsettings.example.json
Normal file
8
appsettings.example.json
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"kafka_bootstrap": "parice.franz:9092",
|
||||||
|
"kafka_name": "twitcher",
|
||||||
|
"port": 8420,
|
||||||
|
"clientId": "plz? :>",
|
||||||
|
"clientSecret": "not synesthesia. It'll come to me. the one where you believe there is no reality, only perception. that's The Secret.",
|
||||||
|
"public_uri": "https://google.com"
|
||||||
|
}
|
14
twitcher.csproj
Normal file
14
twitcher.csproj
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<OutputType>Exe</OutputType>
|
||||||
|
<TargetFramework>net5.0</TargetFramework>
|
||||||
|
<RestoreSources>$(RestoreSources);../packages/nuget/;https://api.nuget.org/v3/index.json</RestoreSources>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
||||||
|
<PackageReference Include="silvermeddlists.franz" Version="0.0.5" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
Loading…
Reference in New Issue
Block a user