Added Support for Debug Logging

Updated a couple of variables for better memory use.
Added Support for ignoring specific streamers
Moved to using a SingletonFactory for Singlton Classes
No Longer Using MessageBox for disconnected message
Added better detection for disconnected to stop it poping with other problems
Added Window and menu option to open window to manage ignored streamers
This commit is contained in:
Martin Barker
2023-02-21 21:25:38 +00:00
parent 03b620ecec
commit 49ea2ba2a6
25 changed files with 548 additions and 208 deletions

View File

@@ -0,0 +1,38 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace TwitchDesktopNotifications.Core
{
public class Logger : SingletonFactory<Logger>, Singleton
{
private string _name;
private StreamWriter sw;
public Logger()
{
_name = DateTime.Now.ToString("dd_mm_yyyy HH_mm")+".log";
}
~Logger()
{
if (sw != null)
{
sw.Flush();
sw.Close();
}
}
public StreamWriter Writer {
get {
if(sw == null)
{
sw = new StreamWriter(_name);
}
return sw;
}
}
}
}

View File

@@ -10,12 +10,14 @@ using Windows.Foundation.Collections;
using System.Diagnostics; using System.Diagnostics;
using System.ComponentModel; using System.ComponentModel;
using Windows.UI.Notifications; using Windows.UI.Notifications;
using System.Net.Http;
namespace TwitchDesktopNotifications.Core namespace TwitchDesktopNotifications.Core
{ {
internal class Notification public class Notification : SingletonFactory<Notification>, Singleton
{ {
private Notification() { private WebClient webClient = new WebClient();
public Notification() {
ToastNotificationManagerCompat.OnActivated += toastArgs => ToastNotificationManagerCompat.OnActivated += toastArgs =>
{ {
// Obtain the arguments from the notification // Obtain the arguments from the notification
@@ -34,22 +36,16 @@ namespace TwitchDesktopNotifications.Core
myProcess.StartInfo.UseShellExecute = true; myProcess.StartInfo.UseShellExecute = true;
myProcess.StartInfo.FileName = args["streamerUrl"]; myProcess.StartInfo.FileName = args["streamerUrl"];
myProcess.Start(); myProcess.Start();
}else if( args.Contains("action") && args["action"] == "ignore")
{
NotifyManager.AddStreamerToIgnoreList(args["streamerName"]);
} }
}catch(Exception ex) { } }catch(Exception ex) {
Logger.GetInstance().Writer.WriteLine(ex.ToString());
}
}; };
} }
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) public void sendNotification(String streamerName, String streamerUrl, String profilePic, String streamThumbnail, String title)
{ {
String FilePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "TwitchNotify"); String FilePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "TwitchNotify");
@@ -60,52 +56,64 @@ namespace TwitchDesktopNotifications.Core
string fileNameProfilePic = profilePic.Split("/").Last(); string fileNameProfilePic = profilePic.Split("/").Last();
if (!File.Exists(FilePath + "/" + fileNameProfilePic)) if (!File.Exists(FilePath + "/" + fileNameProfilePic))
{ {
(new WebClient()).DownloadFile(new Uri(profilePic), FilePath + "/" + fileNameProfilePic); webClient.DownloadFile(new Uri(profilePic), FilePath + "/" + fileNameProfilePic);
} }
// download there profile picture // download there profile picture
string fileNameThumbnailPic = streamThumbnail.Split("/").Last(); string fileNameThumbnailPic = streamThumbnail.Split("/").Last();
(new WebClient()).DownloadFile(new Uri(streamThumbnail), webClient.DownloadFile(new Uri(streamThumbnail),
FilePath + "/" + fileNameThumbnailPic FilePath + "/" + fileNameThumbnailPic
); );
var builder = new ToastContentBuilder() if (NotifyManager.ShouldNotify(streamerName))
.AddArgument("streamerUrl", streamerUrl)
.AddArgument("thumbnail_path", FilePath + "/" + fileNameThumbnailPic)
.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); var builder = new ToastContentBuilder()
toast.Dismissed += (ToastNotification sender, ToastDismissedEventArgs args) => .AddArgument("streamerUrl", streamerUrl)
.AddArgument("streamerName", streamerName)
.AddArgument("thumbnail_path", FilePath + "/" + fileNameThumbnailPic)
.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 ")
.AddArgument("action", "watch")
.SetBackgroundActivation())
.AddButton(new ToastButton()
.SetContent("Dismiss")
.AddArgument("action", "nothing")
.SetBackgroundActivation())
.AddButton(new ToastButton()
.SetContent("Ignore")
.AddArgument("action", "ignore")
.SetBackgroundActivation());
if (title != "")
{ {
try builder.AddText(title);
{ }
File.Delete(FilePath + "/" + fileNameThumbnailPic); builder.Show(toast =>
}catch(Exception) { }
};
toast.Activated += (ToastNotification sender, object args) =>
{ {
try toast.ExpirationTime = DateTime.Now.AddSeconds(15);
toast.Dismissed += (ToastNotification sender, ToastDismissedEventArgs args) =>
{ {
File.Delete(FilePath + "/" + fileNameThumbnailPic); try
} {
catch (Exception) { } File.Delete(FilePath + "/" + fileNameThumbnailPic);
}; }
}); catch (Exception) { }
builder = null;
};
toast.Activated += (ToastNotification sender, object args) =>
{
try
{
File.Delete(FilePath + "/" + fileNameThumbnailPic);
}
catch (Exception) { }
builder = null;
};
});
}
} }
} }
} }

View File

@@ -1,8 +1,10 @@
using System; using Microsoft.Toolkit.Uwp.Notifications;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Xml.Linq;
using TwitchDesktopNotifications.JsonStructure.Helix; using TwitchDesktopNotifications.JsonStructure.Helix;
namespace TwitchDesktopNotifications.Core namespace TwitchDesktopNotifications.Core
@@ -10,39 +12,18 @@ namespace TwitchDesktopNotifications.Core
internal class NotifyManager internal class NotifyManager
{ {
public Boolean shouldNotify(String streamerName) public static Boolean ShouldNotify(String streamerName)
{ {
return notifyAll || !DataStore.GetInstance().Store.SteamersToNotify.Streamers.Contains(streamerName); if(DataStore.GetInstance().Store.SteamersToIgnore == null || DataStore.GetInstance().Store.SteamersToIgnore.Streamers == null) {
} return true;
public void AddStreamerToNotifyList(String streamerName)
{
DataStore.GetInstance().Store.SteamersToNotify.Streamers.Add(streamerName);
DataStore.GetInstance().Save();
}
public void RemoveStreamerToNotifyList(String streamerName)
{
DataStore.GetInstance().Store.SteamersToNotify.Streamers.Remove(streamerName);
DataStore.GetInstance().Save();
}
public void ClearListOfEnabled()
{
DataStore.GetInstance().Store.SteamersToNotify.Streamers = new List<string>();
DataStore.GetInstance().Save();
}
public bool notifyAll
{
get
{
return DataStore.GetInstance().Store.SteamersToNotify.notifyAll;
}
set {
DataStore.GetInstance().Store.SteamersToNotify.notifyAll = value;
DataStore.GetInstance().Save();
} }
return !UIStreamer.GetCreateStreamer(streamerName).IsIgnored;
}
public static void AddStreamerToIgnoreList(string streamerName)
{
UIStreamer.GetCreateStreamer(streamerName).IsIgnored = true;
DataStore.GetInstance().Save();
} }
} }
} }

View File

@@ -0,0 +1,26 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace TwitchDesktopNotifications.Core
{
public interface Singleton
{
}
public class SingletonFactory<T> where T : Singleton, new()
{
public static Dictionary<String, Singleton> store = new Dictionary<String, Singleton>();
public static T GetInstance(){
string name = typeof(T).FullName;
if (!store.ContainsKey(name))
{
store.Add(name, new T());
}
return (T)store[name];
}
}
}

View File

@@ -9,11 +9,30 @@ using TwitchDesktopNotifications.JsonStructure.Helix;
namespace TwitchDesktopNotifications.Core namespace TwitchDesktopNotifications.Core
{ {
internal class TwitcherRefreshException : Exception
{
public TwitcherRefreshException(string? message, Exception? innerException) : base(message, innerException) {
}
}
internal class TwitchFetcher internal class TwitchFetcher
{ {
private TwitchFetcher() { private TwitchFetcher() {
} }
ReconnectionNeeded rnFrm;
public void OpenFailedNotification()
{
if (rnFrm == null)
{
rnFrm = new ReconnectionNeeded();
}
if (rnFrm.IsActive)
{
rnFrm.Show();
}
}
public static TwitchFetcher instance { get; private set; } public static TwitchFetcher instance { get; private set; }
List <StreamsData> currentlyLive = null; List <StreamsData> currentlyLive = null;
@@ -46,36 +65,55 @@ namespace TwitchDesktopNotifications.Core
{ {
throw new Exception("Not Authenticated"); throw new Exception("Not Authenticated");
} }
if (DataStore.GetInstance().Store.Authentication.ExpiresAsDate <= DateTime.UtcNow) if (DataStore.GetInstance().Store.Authentication.ExpiresAsDate <= DateTime.UtcNow)
{ {
Refresh(); Refresh();
} }
WebRequest request = WebRequest.Create("https://api.twitch.tv/" + endpoint); try
request.Method = "GET"; {
request.Headers[HttpRequestHeader.Authorization] = String.Format("Bearer {0}", DataStore.GetInstance().Store.Authentication.AccessToken); WebRequest request = WebRequest.Create("https://api.twitch.tv/" + endpoint);
request.Headers["Client-ID"] = TwitchDetails.TwitchClientID; request.Method = "GET";
WebResponse response = request.GetResponse(); request.Headers[HttpRequestHeader.Authorization] = String.Format("Bearer {0}", DataStore.GetInstance().Store.Authentication.AccessToken);
Stream dataStream = response.GetResponseStream(); request.Headers["Client-ID"] = TwitchDetails.TwitchClientID;
StreamReader reader = new StreamReader(dataStream); WebResponse response = request.GetResponse();
string responseFromServer = reader.ReadToEnd(); Stream dataStream = response.GetResponseStream();
reader.Close(); StreamReader reader = new StreamReader(dataStream);
dataStream.Close(); string responseFromServer = reader.ReadToEnd();
response.Close(); reader.Close();
dataStream.Close();
response.Close();
return JsonSerializer.Deserialize<T>(responseFromServer); return JsonSerializer.Deserialize<T>(responseFromServer);
}
catch (TwitcherRefreshException ex)
{
OpenFailedNotification();
}
catch(Exception ex)
{
Logger.GetInstance().Writer.WriteLineAsync(ex.ToString());
}
return default(T);
} }
public void FetchCurrentUser() public void FetchCurrentUser()
{ {
try try
{ {
DataStore.GetInstance().Store.UserData = MakeRequest<User>("helix/users").Data[0]; var UserList = MakeRequest<User>("helix/users");
DataStore.GetInstance().Save(); if (UserList.Data.Count > 0) {
}catch(System.Exception ex) DataStore.GetInstance().Store.UserData = UserList.Data[0];
DataStore.GetInstance().Save();
}
}
catch (TwitcherRefreshException ex)
{ {
MessageBox.Show("Twitch Connection not authenticated you need to Reconnect it.", "Twitch Notify"); OpenFailedNotification();
}
catch (Exception ex)
{
Logger.GetInstance().Writer.WriteLineAsync(ex.ToString());
} }
} }
@@ -83,10 +121,19 @@ namespace TwitchDesktopNotifications.Core
{ {
try try
{ {
return MakeRequest<User>("helix/users?id=" + user_id).Data[0]; var Response = MakeRequest<User>("helix/users?id=" + user_id);
}catch(System.Exception ex) if (Response.Data.Count > 0)
{
return Response.Data[0];
}
}
catch (TwitcherRefreshException ex)
{ {
MessageBox.Show("Twitch Connection not authenticated you need to Reconnect it.", "Twitch Notify"); OpenFailedNotification();
}
catch (Exception ex)
{
Logger.GetInstance().Writer.WriteLineAsync(ex.ToString());
} }
return null; return null;
} }
@@ -104,7 +151,7 @@ namespace TwitchDesktopNotifications.Core
string QueryUrl = "helix/streams/followed?first=100&user_id=" + DataStore.GetInstance().Store.UserData.UserId; string QueryUrl = "helix/streams/followed?first=100&user_id=" + DataStore.GetInstance().Store.UserData.UserId;
Streams following = MakeRequest<Streams>(QueryUrl); Streams following = MakeRequest<Streams>(QueryUrl);
if (currentlyLive != null) if (following != null && currentlyLive != null)
{ {
following.Data.ForEach(x => following.Data.ForEach(x =>
{ {
@@ -124,49 +171,53 @@ namespace TwitchDesktopNotifications.Core
} }
currentlyLive = following.Data; currentlyLive = following.Data;
}catch(System.Exception ex)
{
if (!ex.Message.Contains("Notification"))
{
MessageBox.Show("Twitch Connection not authenticated you need to Reconnect it.", "Twitch Notify");
}
else
{
MessageBox.Show("Unable to use Windows Notifications.", "Twitch Notify");
}
} }
} catch (TwitcherRefreshException ex)
{
OpenFailedNotification();
}
catch (Exception ex)
{
Logger.GetInstance().Writer.WriteLineAsync(ex.ToString());
}
}
public void Refresh() public void Refresh()
{ {
Dictionary<string, string> postData = new Dictionary<string, string>(); try
{
Dictionary<string, string> postData = new Dictionary<string, string>();
postData["client_id"] = TwitchDetails.TwitchClientID; postData["client_id"] = TwitchDetails.TwitchClientID;
postData["client_secret"] = TwitchDetails.TwitchClientSecret; postData["client_secret"] = TwitchDetails.TwitchClientSecret;
postData["grant_type"] = "refresh_token"; postData["grant_type"] = "refresh_token";
postData["refresh_token"] = DataStore.GetInstance().Store.Authentication.RefreshToken; postData["refresh_token"] = DataStore.GetInstance().Store.Authentication.RefreshToken;
byte[] byteArray = buildPostData(postData); byte[] byteArray = buildPostData(postData);
WebRequest request = WebRequest.Create("https://id.twitch.tv/oauth2/token"); WebRequest request = WebRequest.Create("https://id.twitch.tv/oauth2/token");
request.Method = "POST"; request.Method = "POST";
request.ContentType = "application/x-www-form-urlencoded"; request.ContentType = "application/x-www-form-urlencoded";
request.ContentLength = byteArray.Length; request.ContentLength = byteArray.Length;
Stream dataStream = request.GetRequestStream(); Stream dataStream = request.GetRequestStream();
dataStream.Write(byteArray, 0, byteArray.Length); dataStream.Write(byteArray, 0, byteArray.Length);
dataStream.Close(); dataStream.Close();
WebResponse response = request.GetResponse(); WebResponse response = request.GetResponse();
dataStream = response.GetResponseStream(); dataStream = response.GetResponseStream();
StreamReader reader = new StreamReader(dataStream); StreamReader reader = new StreamReader(dataStream);
string responseFromServer = reader.ReadToEnd(); string responseFromServer = reader.ReadToEnd();
reader.Close(); reader.Close();
dataStream.Close(); dataStream.Close();
response.Close(); response.Close();
DataStore.GetInstance().Store.Authentication = JsonSerializer.Deserialize<Authentication>(responseFromServer); DataStore.GetInstance().Store.Authentication = JsonSerializer.Deserialize<Authentication>(responseFromServer);
DateTime unixStart = DateTime.SpecifyKind(new DateTime(1970, 1, 1), DateTimeKind.Utc); 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().Store.Authentication.ExpiresAt = (long)Math.Floor((DateTime.Now.AddSeconds(DataStore.GetInstance().Store.Authentication.ExpiresSeconds) - unixStart).TotalMilliseconds);
DataStore.GetInstance().Save(); DataStore.GetInstance().Save();
}catch(Exception e)
{
throw new TwitcherRefreshException("Unable to refresh", e);
}
} }
async public void BeginConnection() async public void BeginConnection()

View File

@@ -0,0 +1,69 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.Json.Serialization;
using System.Threading.Tasks;
using System.Windows.Documents;
namespace TwitchDesktopNotifications.Core
{
public class UIStreamer
{
public UIStreamer() {
}
public static UIStreamer GetCreateStreamer(string name)
{
if (DataStore.GetInstance().Store.SteamersToIgnore == null)
{
DataStore.GetInstance().Store.SteamersToIgnore = new JsonStructure.SteamersToIgnore();
}
if (DataStore.GetInstance().Store.SteamersToIgnore.Streamers == null)
{
DataStore.GetInstance().Store.SteamersToIgnore.Streamers = new List<UIStreamer>();
}
UIStreamer strmr = null;
try
{
strmr = DataStore.GetInstance().Store.SteamersToIgnore.Streamers.Where((UIStreamer strmr) => strmr.Name == name).First();
}
catch { }
finally
{
if (strmr == null)
{
strmr = new UIStreamer() { IsIgnored = false, Name = name };
DataStore.GetInstance().Store.SteamersToIgnore.Streamers.Add(strmr);
DataStore.GetInstance().Save();
}
}
return strmr;
}
[JsonIgnore]
public List<UIStreamer> StreamersToIgnore
{
get
{
return DataStore.GetInstance().Store.SteamersToIgnore.Streamers;
}
}
[JsonPropertyName("IsIgnored")]
public bool IsIgnored { get; set; } = false;
[JsonPropertyName("StreamerName")]
public string Name { get; set; }
[JsonIgnore]
public string Link {
get {
return String.Format("https://www.twitch.tv/{0}", Name);
}
}
}
}

View File

@@ -5,23 +5,12 @@ using System.IO;
namespace TwitchDesktopNotifications.Core namespace TwitchDesktopNotifications.Core
{ {
internal class WebServer public class WebServer : SingletonFactory<WebServer>, Singleton
{ {
public int Port = 32584; public int Port = 32584;
private HttpListener listener; 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 TwitchCode { get; private set; }
public String TwitchState { get; set; } public String TwitchState { get; set; }

View File

@@ -1,14 +1,12 @@
using System.Text.Json; using System.Text.Json;
using TwitchDesktopNotifications.JsonStructure; using TwitchDesktopNotifications.JsonStructure;
using System.IO; using System.IO;
using TwitchDesktopNotifications.Core;
namespace TwitchDesktopNotifications namespace TwitchDesktopNotifications
{ {
internal class DataStore public class DataStore : SingletonFactory<DataStore>, Singleton
{ {
private DataStore() { }
public static DataStore Instance { get; private set; }
private Store _store; private Store _store;
public JsonStructure.Store Store { public JsonStructure.Store Store {
get { get {
@@ -25,15 +23,6 @@ namespace TwitchDesktopNotifications
public bool isLoaded { get; private set; } public bool isLoaded { get; private set; }
public static DataStore GetInstance()
{
if(Instance == null)
{
Instance = new DataStore();
}
return Instance;
}
public void Save() public void Save()
{ {
String FilePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "TwitchNotify"); String FilePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "TwitchNotify");
@@ -41,29 +30,30 @@ namespace TwitchDesktopNotifications
string fileContent = JsonSerializer.Serialize<JsonStructure.Store>(Store); string fileContent = JsonSerializer.Serialize<JsonStructure.Store>(Store);
Console.WriteLine("I'm trying to save:");
Console.WriteLine(fileContent);
Console.WriteLine("to {0}", FilePath + "/" + FileName);
Directory.CreateDirectory(FilePath); Directory.CreateDirectory(FilePath);
File.WriteAllText(FilePath + "/" + FileName, fileContent); File.WriteAllText(FilePath + "/" + FileName, fileContent);
} }
public void Load() { public void Load() {
String FilePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "TwitchNotify"); if (!isLoaded)
String FileName = "store.json";
Directory.CreateDirectory(FilePath);
if(File.Exists(FilePath+"/"+ FileName)) {
string fileContent = File.ReadAllText(FilePath+"/"+ FileName);
Store = JsonSerializer.Deserialize<JsonStructure.Store>(fileContent);
}
else
{ {
Store = new JsonStructure.Store(); String FilePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "TwitchNotify");
String FileName = "store.json";
Directory.CreateDirectory(FilePath);
if (File.Exists(FilePath + "/" + FileName))
{
string fileContent = File.ReadAllText(FilePath + "/" + FileName);
Store = JsonSerializer.Deserialize<JsonStructure.Store>(fileContent);
}
else
{
Store = new JsonStructure.Store();
}
isLoaded = true;
} }
isLoaded= true;
} }
} }
} }

View File

@@ -7,7 +7,7 @@ using System.Threading.Tasks;
namespace TwitchDesktopNotifications.JsonStructure namespace TwitchDesktopNotifications.JsonStructure
{ {
internal class Authentication public class Authentication
{ {
[JsonPropertyName("access_token")] [JsonPropertyName("access_token")]
public string AccessToken { get; set; } public string AccessToken { get; set; }

View File

@@ -7,7 +7,7 @@ using System.Threading.Tasks;
namespace TwitchDesktopNotifications.JsonStructure.Helix namespace TwitchDesktopNotifications.JsonStructure.Helix
{ {
internal class Pagination public class Pagination
{ {
public Pagination() { } public Pagination() { }

View File

@@ -7,7 +7,7 @@ using System.Threading.Tasks;
namespace TwitchDesktopNotifications.JsonStructure.Helix namespace TwitchDesktopNotifications.JsonStructure.Helix
{ {
internal class Streams public class Streams
{ {
public Streams() { } public Streams() { }

View File

@@ -7,7 +7,7 @@ using System.Threading.Tasks;
namespace TwitchDesktopNotifications.JsonStructure.Helix namespace TwitchDesktopNotifications.JsonStructure.Helix
{ {
internal class StreamsData public class StreamsData
{ {
public StreamsData() { } public StreamsData() { }

View File

@@ -7,7 +7,7 @@ using System.Threading.Tasks;
namespace TwitchDesktopNotifications.JsonStructure.Helix namespace TwitchDesktopNotifications.JsonStructure.Helix
{ {
internal class User public class User
{ {
public User() { } public User() { }

View File

@@ -7,7 +7,7 @@ using System.Threading.Tasks;
namespace TwitchDesktopNotifications.JsonStructure.Helix namespace TwitchDesktopNotifications.JsonStructure.Helix
{ {
internal class UserData public class UserData
{ {
public UserData() { } public UserData() { }

View File

@@ -7,7 +7,7 @@ using System.Threading.Tasks;
namespace TwitchDesktopNotifications.JsonStructure.Helix namespace TwitchDesktopNotifications.JsonStructure.Helix
{ {
internal class UserFollows public class UserFollows
{ {
public UserFollows() { } public UserFollows() { }

View File

@@ -7,7 +7,7 @@ using System.Threading.Tasks;
namespace TwitchDesktopNotifications.JsonStructure.Helix namespace TwitchDesktopNotifications.JsonStructure.Helix
{ {
internal class UsersFollowsData public class UsersFollowsData
{ {
public UsersFollowsData() { } public UsersFollowsData() { }

View File

@@ -0,0 +1,11 @@
using System.Text.Json.Serialization;
using TwitchDesktopNotifications.Core;
namespace TwitchDesktopNotifications.JsonStructure
{
public class SteamersToIgnore
{
[JsonPropertyName("IgnoredStreamers")]
public List<UIStreamer> Streamers { get; set; } = new List<UIStreamer>();
}
}

View File

@@ -1,13 +0,0 @@
using System.Text.Json.Serialization;
namespace TwitchDesktopNotifications.JsonStructure
{
internal class SteamersToNotify
{
[JsonPropertyName("notifyAll")]
public Boolean notifyAll = true;
[JsonPropertyName("notify")]
public List<String> Streamers { get; set; } = new List<String>();
}
}

View File

@@ -8,17 +8,28 @@ using TwitchDesktopNotifications.JsonStructure.Helix;
namespace TwitchDesktopNotifications.JsonStructure namespace TwitchDesktopNotifications.JsonStructure
{ {
internal class Store public class Store
{ {
public Store() { } public Store() { }
[JsonPropertyName("ignore")]
public SteamersToIgnore ignore { get; set; }
[JsonPropertyName("authentication")] [JsonPropertyName("authentication")]
public Authentication Authentication { get; set; } public Authentication Authentication { get; set; }
[JsonPropertyName("user_data")] [JsonPropertyName("user_data")]
public UserData UserData { get; set; } public UserData UserData { get; set; }
[JsonPropertyName("notifications_for")] [JsonIgnore]
public SteamersToNotify SteamersToNotify { get; set; }; public SteamersToIgnore SteamersToIgnore {
get {
if(ignore == null) { ignore = new SteamersToIgnore(); }
return ignore;
}
set {
ignore = value;
}
}
} }
} }

View File

@@ -0,0 +1,50 @@
<Window x:Class="TwitchDesktopNotifications.ManageIgnores"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:TwitchDesktopNotifications"
xmlns:core="clr-namespace:TwitchDesktopNotifications.Core"
mc:Ignorable="d"
Title="Manage Ignored Streamers" Height="435" Width="395" ResizeMode="NoResize">
<Window.DataContext>
<core:UIStreamer />
</Window.DataContext>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="10" />
<ColumnDefinition Width="120" />
<ColumnDefinition Width="120" />
<ColumnDefinition Width="120" />
<ColumnDefinition Width="10" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="10" />
<RowDefinition Height="30" />
<RowDefinition Height="10" />
<RowDefinition Height="300" />
<RowDefinition Height="10" />
<RowDefinition Height="25" />
<RowDefinition Height="10" />
</Grid.RowDefinitions>
<DataGrid
Grid.Column="1" Grid.ColumnSpan="3"
Grid.RowSpan="1" Grid.Row="3"
x:Name="dgrdIgnore" ItemsSource="{Binding StreamersToIgnore}" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTextColumn Header="Streamer Name" Binding="{Binding Name}" IsReadOnly="True" />
<DataGridHyperlinkColumn Header="Streamer Link" Binding="{Binding Link}" IsReadOnly="True" />
<DataGridCheckBoxColumn Header="Ignore" Binding="{Binding IsIgnored}" IsReadOnly="False">
<DataGridCheckBoxColumn.CellStyle>
<Style>
<EventSetter Event="CheckBox.Checked" Handler="OnChecked" />
</Style>
</DataGridCheckBoxColumn.CellStyle>
</DataGridCheckBoxColumn>
</DataGrid.Columns>
</DataGrid>
<Button Content="Close" Grid.Column="3" Grid.Row="5" Click="CloseBtn_Click" />
<TextBlock Grid.Column="1" Grid.Row="1" Grid.ColumnSpan="3" TextWrapping="Wrap" Text="Changes to the ignore list are automatically saved." VerticalAlignment="Top"/>
<TextBlock Grid.Column="1" Grid.Row="1" Grid.ColumnSpan="3" TextWrapping="Wrap" Text="If you can't see a streamer Twitch Notify has not seen them." VerticalAlignment="Bottom"/>
</Grid>
</Window>

View File

@@ -0,0 +1,30 @@
using System.Windows;
using System.Windows.Forms;
using TwitchDesktopNotifications.Core;
namespace TwitchDesktopNotifications
{
/// <summary>
/// Interaction logic for ManageIgnores.xaml
/// </summary>
public partial class ManageIgnores : Window
{
private bool updated = false;
public ManageIgnores()
{
InitializeComponent();
}
List<UIStreamer> StreamersToIgnore = DataStore.GetInstance().Store.SteamersToIgnore.Streamers;
private void CloseBtn_Click(object sender, RoutedEventArgs e)
{
this.Close();
}
private void OnChecked(object sender, RoutedEventArgs e)
{
updated= true;
}
}
}

View File

@@ -18,6 +18,7 @@ internal class Program
private static NotifyIcon notifyIcon; private static NotifyIcon notifyIcon;
private static ContextMenuStrip cms; private static ContextMenuStrip cms;
private static ManageIgnores manageIgnores;
public static void Ws_CodeRecived(object? sender, EventArgs e) public static void Ws_CodeRecived(object? sender, EventArgs e)
{ {
@@ -45,6 +46,21 @@ internal class Program
TriggerAuthentication(); TriggerAuthentication();
} }
protected static void ManageIgnores_Click(object? sender, System.EventArgs e)
{
if (manageIgnores == null) {
manageIgnores = new ManageIgnores();
manageIgnores.Closed += ManageIgnores_Closed;
}
manageIgnores.Show();
manageIgnores.Focus();
}
private static void ManageIgnores_Closed(object? sender, EventArgs e)
{
manageIgnores = null;
}
protected static void Quit_Click(object? sender, System.EventArgs e) protected static void Quit_Click(object? sender, System.EventArgs e)
{ {
notifyIcon.Visible = false; notifyIcon.Visible = false;
@@ -62,12 +78,13 @@ internal class Program
{ {
if (isConnecting) if (isConnecting)
{ {
MessageBox.Show("Twitch Connection not authenticated you need to Reconnect it.", "Twitch Notify"); TwitchFetcher.GetInstance().OpenFailedNotification();
} }
} }
} }
private static async Task Main(string[] args) [STAThread]
private static void Main(string[] args)
{ {
try try
{ {
@@ -78,7 +95,8 @@ internal class Program
notifyIcon.Text = "Twitch Notify"; notifyIcon.Text = "Twitch Notify";
cms = new ContextMenuStrip(); cms = new ContextMenuStrip();
cms.Items.Add(new ToolStripMenuItem("Manage Ignores", null, new EventHandler(ManageIgnores_Click)));
cms.Items.Add(new ToolStripSeparator());
cms.Items.Add(new ToolStripMenuItem("Reconnect", null, new EventHandler(Reconnect_Click))); cms.Items.Add(new ToolStripMenuItem("Reconnect", null, new EventHandler(Reconnect_Click)));
cms.Items.Add(new ToolStripSeparator()); cms.Items.Add(new ToolStripSeparator());
cms.Items.Add(new ToolStripMenuItem("Quit", null, new EventHandler(Quit_Click), "Quit")); cms.Items.Add(new ToolStripMenuItem("Quit", null, new EventHandler(Quit_Click), "Quit"));
@@ -91,7 +109,7 @@ internal class Program
TriggerAuthentication(); TriggerAuthentication();
} }
new Thread(() => Thread thread = new Thread(() =>
{ {
while (true) while (true)
{ {
@@ -101,12 +119,14 @@ internal class Program
TwitchFetcher.GetInstance().GetLiveFollowingUsers(); TwitchFetcher.GetInstance().GetLiveFollowingUsers();
} }
} }
}).Start(); });
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
Application.Run(); Application.Run();
} }
catch (Exception e) { catch (Exception e) {
Console.WriteLine(e.ToString()); Logger.GetInstance().Writer.WriteLineAsync(e.ToString());
} }
} }

View File

@@ -0,0 +1,27 @@
<Window x:Class="TwitchDesktopNotifications.ReconnectionNeeded"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:TwitchDesktopNotifications"
mc:Ignorable="d"
Title="Twitch Disconnected" Height="140" Width="335" x:Name="reconnectionNeededWin">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="10" />
<ColumnDefinition Width="100" />
<ColumnDefinition Width="100" />
<ColumnDefinition Width="100" />
<ColumnDefinition Width="10" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="10" />
<RowDefinition Height="40" />
<RowDefinition Height="10" />
<RowDefinition Height="40" />
</Grid.RowDefinitions>
<TextBlock TextWrapping="Wrap" VerticalAlignment="Top" TextAlignment="Center" Text="We have been unable to refresh your twitch connection" Grid.Column="1" Grid.Row="1" Grid.ColumnSpan="3"/>
<TextBlock TextWrapping="Wrap" VerticalAlignment="Center" TextAlignment="Center" Text="Don't worry you just need to reconnect" Grid.Column="1" Grid.Row="1" Grid.ColumnSpan="3"/>
<Button Content="Thanks" HorizontalAlignment="Center" VerticalAlignment="Top" Grid.Column="2" Grid.Row="3" Padding="5" Click="Button_Click"/>
</Grid>
</Window>

View File

@@ -0,0 +1,32 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
namespace TwitchDesktopNotifications
{
/// <summary>
/// Interaction logic for ReconnectionNeeded.xaml
/// </summary>
public partial class ReconnectionNeeded : Window
{
public ReconnectionNeeded()
{
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
this.Close();
}
}
}

View File

@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup />
<ItemGroup>
<Compile Update="ManageIgnores.xaml.cs">
<SubType>Code</SubType>
</Compile>
<Compile Update="ReconnectionNeeded.xaml.cs">
<SubType>Code</SubType>
</Compile>
</ItemGroup>
<ItemGroup>
<Page Update="ManageIgnores.xaml">
<SubType>Designer</SubType>
</Page>
<Page Update="ReconnectionNeeded.xaml">
<SubType>Designer</SubType>
</Page>
</ItemGroup>
</Project>