Inital Commit
This commit is contained in:
85
TwitchDesktopNotifications/Core/Notification.cs
Normal file
85
TwitchDesktopNotifications/Core/Notification.cs
Normal file
@@ -0,0 +1,85 @@
|
||||
using Microsoft.Toolkit.Uwp.Notifications;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.IO;
|
||||
using Windows.Foundation.Collections;
|
||||
using System.Diagnostics;
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace TwitchDesktopNotifications.Core
|
||||
{
|
||||
internal class Notification
|
||||
{
|
||||
private Notification() {
|
||||
ToastNotificationManagerCompat.OnActivated += toastArgs =>
|
||||
{
|
||||
// Obtain the arguments from the notification
|
||||
ToastArguments args = ToastArguments.Parse(toastArgs.Argument);
|
||||
|
||||
try
|
||||
{
|
||||
Process myProcess = new Process();
|
||||
myProcess.StartInfo.UseShellExecute = true;
|
||||
myProcess.StartInfo.FileName = args["streamerUrl"];
|
||||
myProcess.Start();
|
||||
}catch(Exception ex) { }
|
||||
};
|
||||
}
|
||||
|
||||
private static Notification Instance;
|
||||
|
||||
public static Notification GetInstance()
|
||||
{
|
||||
if(Instance == null)
|
||||
{
|
||||
Instance = new Notification();
|
||||
}
|
||||
return Instance;
|
||||
}
|
||||
|
||||
public void sendNotification(String streamerName, String streamerUrl, String profilePic, String streamThumbnail, String title)
|
||||
{
|
||||
String FilePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "TwitchNotify");
|
||||
|
||||
streamThumbnail = streamThumbnail.Replace("{width}", 260.ToString()).Replace("{height}", 147.ToString());
|
||||
|
||||
// download there profile picture
|
||||
string fileNameProfilePic = profilePic.Split("/").Last();
|
||||
(new WebClient()).DownloadFile(new Uri(profilePic), FilePath+"/"+ fileNameProfilePic);
|
||||
|
||||
// download there profile picture
|
||||
string fileNameThumbnailPic = streamThumbnail.Split("/").Last();
|
||||
(new WebClient()).DownloadFile(new Uri(streamThumbnail),
|
||||
FilePath + "/" + fileNameThumbnailPic
|
||||
);
|
||||
|
||||
var builder = new ToastContentBuilder()
|
||||
.AddArgument("streamerUrl", streamerUrl)
|
||||
.AddText(streamerName + " is now live on Twitch")
|
||||
.AddHeroImage(new Uri("file://" + (FilePath + "/" + fileNameThumbnailPic).Replace("\\", "/")))
|
||||
.AddAppLogoOverride(new Uri("file://" + (FilePath + "/" + fileNameProfilePic).Replace("\\", "/")), ToastGenericAppLogoCrop.Circle)
|
||||
.AddButton(new ToastButton()
|
||||
.SetContent("Watch " + streamerName)
|
||||
.AddArgument("action", "watch")
|
||||
.SetBackgroundActivation())
|
||||
.AddButton(new ToastButton()
|
||||
.SetContent("Dismiss")
|
||||
.AddArgument("action", "nothing")
|
||||
.SetBackgroundActivation());
|
||||
|
||||
if(title != "") {
|
||||
builder.AddText(title);
|
||||
}
|
||||
builder.Show(toast =>
|
||||
{
|
||||
toast.ExpirationTime = DateTime.Now.AddSeconds(15);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
212
TwitchDesktopNotifications/Core/TwitchFetcher.cs
Normal file
212
TwitchDesktopNotifications/Core/TwitchFetcher.cs
Normal file
@@ -0,0 +1,212 @@
|
||||
using ABI.System;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Configuration;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Nodes;
|
||||
using System.Threading.Tasks;
|
||||
using System.Web;
|
||||
using TwitchDesktopNotifications.JsonStructure;
|
||||
using TwitchDesktopNotifications.JsonStructure.Helix;
|
||||
using Windows.ApplicationModel.Background;
|
||||
|
||||
namespace TwitchDesktopNotifications.Core
|
||||
{
|
||||
internal class TwitchFetcher
|
||||
{
|
||||
private TwitchFetcher() { }
|
||||
|
||||
public static TwitchFetcher instance { get; private set; }
|
||||
|
||||
string TwitchClientID = ConfigurationManager.AppSettings["TwitchClientID"];
|
||||
string TwitchClientSecret = ConfigurationManager.AppSettings["TwitchClientSecret"];
|
||||
|
||||
List <StreamsData> currentlyLive = null;
|
||||
|
||||
public string guid { get; private set; }
|
||||
|
||||
public static TwitchFetcher GetInstance()
|
||||
{
|
||||
if(instance == null)
|
||||
{
|
||||
instance = new TwitchFetcher();
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
private byte[] buildPostData(Dictionary<string, string> postData)
|
||||
{
|
||||
string content = "";
|
||||
foreach (var pair in postData)
|
||||
{
|
||||
content += HttpUtility.UrlEncode(pair.Key) + "=" + HttpUtility.UrlEncode(pair.Value) + "&";
|
||||
}
|
||||
content = content.TrimEnd('&');
|
||||
return Encoding.UTF8.GetBytes(content);
|
||||
}
|
||||
|
||||
private T MakeRequest<T>(string endpoint)
|
||||
{
|
||||
|
||||
if (DataStore.GetInstance().Store.Authentication.ExpiresAsDate <= DateTime.UtcNow)
|
||||
{
|
||||
Refresh();
|
||||
}
|
||||
|
||||
WebRequest request = WebRequest.Create("https://api.twitch.tv/" + endpoint);
|
||||
request.Method = "GET";
|
||||
request.Headers[HttpRequestHeader.Authorization] = String.Format("Bearer {0}", DataStore.GetInstance().Store.Authentication.AccessToken);
|
||||
request.Headers["Client-ID"] = TwitchClientID;
|
||||
WebResponse response = request.GetResponse();
|
||||
Stream dataStream = response.GetResponseStream();
|
||||
StreamReader reader = new StreamReader(dataStream);
|
||||
string responseFromServer = reader.ReadToEnd();
|
||||
reader.Close();
|
||||
dataStream.Close();
|
||||
response.Close();
|
||||
|
||||
return JsonSerializer.Deserialize<T>(responseFromServer);
|
||||
}
|
||||
|
||||
public void FetchCurrentUser()
|
||||
{
|
||||
try
|
||||
{
|
||||
DataStore.GetInstance().Store.UserData = MakeRequest<User>("helix/users").Data[0];
|
||||
DataStore.GetInstance().Save();
|
||||
}catch(System.Exception ex)
|
||||
{
|
||||
Environment.Exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
public UserData FetchUserData(string user_id)
|
||||
{
|
||||
try
|
||||
{
|
||||
return MakeRequest<User>("helix/users?id=" + user_id).Data[0];
|
||||
}catch(System.Exception ex)
|
||||
{
|
||||
Environment.Exit(1);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void GetLiveFollowingUsers()
|
||||
{
|
||||
try
|
||||
{
|
||||
bool isFinished = false;
|
||||
if (DataStore.GetInstance().Store.UserData == null)
|
||||
{
|
||||
FetchCurrentUser();
|
||||
}
|
||||
|
||||
string QueryUrl = "helix/streams/followed?first=100&user_id=" + DataStore.GetInstance().Store.UserData.UserId;
|
||||
Streams following = MakeRequest<Streams>(QueryUrl);
|
||||
|
||||
if (currentlyLive != null)
|
||||
{
|
||||
following.Data.ForEach(x =>
|
||||
{
|
||||
bool found = false;
|
||||
|
||||
foreach (StreamsData sd in currentlyLive)
|
||||
{
|
||||
if (sd.UserId == x.UserId) found = true;
|
||||
}
|
||||
|
||||
if (!found)
|
||||
{
|
||||
UserData streamer = FetchUserData(x.UserId);
|
||||
Notification.GetInstance().sendNotification(streamer.DisplayName, "https://twitch.tv/" + streamer.UserName, streamer.ProfileImage, x.ThumbnailImg, x.Title);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
currentlyLive = following.Data;
|
||||
}catch(System.Exception ex)
|
||||
{
|
||||
Environment.Exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
public void Refresh()
|
||||
{
|
||||
Dictionary<string, string> postData = new Dictionary<string, string>();
|
||||
|
||||
postData["client_id"] = TwitchClientID;
|
||||
postData["client_secret"] = TwitchClientSecret;
|
||||
postData["grant_type"] = "refresh_token";
|
||||
postData["refresh_token"] = DataStore.GetInstance().Store.Authentication.RefreshToken;
|
||||
|
||||
byte[] byteArray = buildPostData(postData);
|
||||
|
||||
WebRequest request = WebRequest.Create("https://id.twitch.tv/oauth2/token");
|
||||
request.Method = "POST";
|
||||
request.ContentType = "application/x-www-form-urlencoded";
|
||||
request.ContentLength = byteArray.Length;
|
||||
Stream dataStream = request.GetRequestStream();
|
||||
dataStream.Write(byteArray, 0, byteArray.Length);
|
||||
dataStream.Close();
|
||||
WebResponse response = request.GetResponse();
|
||||
dataStream = response.GetResponseStream();
|
||||
StreamReader reader = new StreamReader(dataStream);
|
||||
string responseFromServer = reader.ReadToEnd();
|
||||
reader.Close();
|
||||
dataStream.Close();
|
||||
response.Close();
|
||||
|
||||
DataStore.GetInstance().Store.Authentication = JsonSerializer.Deserialize<Authentication>(responseFromServer);
|
||||
DateTime unixStart = DateTime.SpecifyKind(new DateTime(1970, 1, 1), DateTimeKind.Utc);
|
||||
DataStore.GetInstance().Store.Authentication.ExpiresAt = (long)Math.Floor((DateTime.Now.AddSeconds(DataStore.GetInstance().Store.Authentication.ExpiresSeconds) - unixStart).TotalMilliseconds);
|
||||
DataStore.GetInstance().Save();
|
||||
}
|
||||
|
||||
async public void BeginConnection()
|
||||
{
|
||||
guid = Guid.NewGuid().ToString();
|
||||
WebServer.GetInstance().TwitchState = guid;
|
||||
Process myProcess = new Process();
|
||||
myProcess.StartInfo.UseShellExecute = true;
|
||||
myProcess.StartInfo.FileName = String.Format("https://id.twitch.tv/oauth2/authorize?&redirect_uri=http://localhost:32584/twitchRedirect&scope=user:read:subscriptions%20user:read:follows%20user:read:email%20openid&response_type=code&state={0}&nonce={1}&client_id={2}", guid, guid, TwitchClientID);
|
||||
myProcess.Start();
|
||||
}
|
||||
|
||||
public string endConnection(string code)
|
||||
{
|
||||
Dictionary<string, string> postData = new Dictionary<string, string>();
|
||||
|
||||
postData["client_id"] = TwitchClientID;
|
||||
postData["client_secret"] = TwitchClientSecret;
|
||||
postData["grant_type"] = "authorization_code";
|
||||
postData["redirect_uri"] = "http://localhost:32584/twitchRedirect";
|
||||
postData["code"] = code;
|
||||
|
||||
byte[] byteArray = buildPostData(postData);
|
||||
|
||||
WebRequest request = WebRequest.Create("https://id.twitch.tv/oauth2/token");
|
||||
request.Method = "POST";
|
||||
request.ContentType = "application/x-www-form-urlencoded";
|
||||
request.ContentLength = byteArray.Length;
|
||||
Stream dataStream = request.GetRequestStream();
|
||||
dataStream.Write(byteArray, 0, byteArray.Length);
|
||||
dataStream.Close();
|
||||
WebResponse response = request.GetResponse();
|
||||
dataStream = response.GetResponseStream();
|
||||
StreamReader reader = new StreamReader(dataStream);
|
||||
string responseFromServer = reader.ReadToEnd();
|
||||
reader.Close();
|
||||
dataStream.Close();
|
||||
response.Close();
|
||||
return responseFromServer;
|
||||
}
|
||||
}
|
||||
}
|
||||
112
TwitchDesktopNotifications/Core/WebServer.cs
Normal file
112
TwitchDesktopNotifications/Core/WebServer.cs
Normal file
@@ -0,0 +1,112 @@
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
using System.Web;
|
||||
using System.IO;
|
||||
|
||||
namespace TwitchDesktopNotifications.Core
|
||||
{
|
||||
internal class WebServer
|
||||
{
|
||||
public int Port = 32584;
|
||||
|
||||
private HttpListener listener;
|
||||
|
||||
private static WebServer Instance;
|
||||
|
||||
public static WebServer GetInstance()
|
||||
{
|
||||
if(Instance == null)
|
||||
{
|
||||
Instance= new WebServer();
|
||||
}
|
||||
return Instance;
|
||||
}
|
||||
|
||||
public String TwitchCode { get; private set; }
|
||||
public String TwitchState { get; set; }
|
||||
|
||||
public void Start()
|
||||
{
|
||||
listener = new HttpListener();
|
||||
listener.Prefixes.Add("http://127.0.0.1:" + Port.ToString() + "/");
|
||||
listener.Prefixes.Add("http://localhost:" + Port.ToString() + "/");
|
||||
listener.Start();
|
||||
new Thread(new ThreadStart(ThreadManagedServer)).Start();
|
||||
}
|
||||
|
||||
public event EventHandler CodeRecived;
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
listener.Stop();
|
||||
}
|
||||
|
||||
private void RespondConnection(HttpListenerRequest request, HttpListenerResponse response)
|
||||
{
|
||||
var query = HttpUtility.ParseQueryString(request.Url.Query);
|
||||
if (request.HttpMethod == "GET" && query["state"] == this.TwitchState)
|
||||
{
|
||||
this.TwitchCode = query["code"];
|
||||
response.StatusCode = (int)HttpStatusCode.OK;
|
||||
response.ContentType = "text/html";
|
||||
response.OutputStream.Write(Encoding.ASCII.GetBytes("<!DOCTYPE html><html><head><title>Twitch Connected!</title><style>p.title{font-size:20px;font-weight:bold;margin-top:0px;}.container{width:240px;border:2px solid #bf94ff;padding:20px;margin:auto;border-radius:10px;}button{margin-left:195px;}</style></head><body><div class=\"container\"><p class=\"title\">Twitch Desktop Notification</p><p class=\"msg\">Twitch has been success fully connected. Please close this tab.</p><button onclick=\"javascript:window.close();\">Close</button></div></body></html>"));
|
||||
response.OutputStream.Close();
|
||||
CodeRecived?.Invoke(this, new EventArgs());
|
||||
}
|
||||
else
|
||||
{;
|
||||
response.StatusCode = (int)HttpStatusCode.Forbidden;
|
||||
response.ContentType = "text/html";
|
||||
response.OutputStream.Write(Encoding.ASCII.GetBytes("<!DOCTYPE html><html><head><title>State Missmatch</title></head><body><h1>State Missmatch</h1><p>State does not match up preventing XSS.</p></body></html>"));
|
||||
response.OutputStream.Close();
|
||||
}
|
||||
}
|
||||
|
||||
private void RespondFavicon(HttpListenerResponse response)
|
||||
{
|
||||
response.StatusCode = (int)HttpStatusCode.OK;
|
||||
response.ContentType = "image/x-icon";
|
||||
response.OutputStream.Write(File.ReadAllBytes("Assets/icon.ico"));
|
||||
response.OutputStream.Close();
|
||||
}
|
||||
|
||||
private void processRequestThread(object? obj)
|
||||
{
|
||||
HttpListenerContext context = (HttpListenerContext)obj;
|
||||
HttpListenerRequest request = context.Request;
|
||||
|
||||
if (request.Url.AbsolutePath == "/favicon.ico")
|
||||
{
|
||||
RespondFavicon(context.Response);
|
||||
}
|
||||
else if (request.Url.AbsolutePath == "/twitchRedirect")
|
||||
{
|
||||
RespondConnection(request, context.Response);
|
||||
}
|
||||
else
|
||||
{
|
||||
HttpListenerResponse response = context.Response;
|
||||
response.StatusCode = (int)HttpStatusCode.NotFound;
|
||||
response.ContentType = "text/html";
|
||||
response.OutputStream.Write(Encoding.ASCII.GetBytes("<!DOCTYPE html><html><head><title>Not Found</title></head><body><h1>Not Found</h1><p>File not found</p></body></html>"));
|
||||
response.OutputStream.Close();
|
||||
}
|
||||
}
|
||||
|
||||
private void ThreadManagedServer()
|
||||
{
|
||||
while (listener.IsListening)
|
||||
{
|
||||
try
|
||||
{
|
||||
HttpListenerContext context = listener.GetContext();
|
||||
ParameterizedThreadStart pts = new ParameterizedThreadStart(processRequestThread);
|
||||
pts.Invoke(context);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user