3 Commits

Author SHA1 Message Date
Martin Barker
45d4dad22a Updated Ignore form to be a little cleaner, and hyperlinks now work. 2023-02-21 22:34:56 +00:00
Martin Barker
49ea2ba2a6 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
2023-02-21 21:25:38 +00:00
Martin Barker
03b620ecec Started work on #1 2023-02-03 18:59:07 +00:00
24 changed files with 581 additions and 154 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.ComponentModel;
using Windows.UI.Notifications;
using System.Net.Http;
namespace TwitchDesktopNotifications.Core
{
internal class Notification
public class Notification : SingletonFactory<Notification>, Singleton
{
private Notification() {
private WebClient webClient = new WebClient();
public Notification() {
ToastNotificationManagerCompat.OnActivated += toastArgs =>
{
// Obtain the arguments from the notification
@@ -34,22 +36,16 @@ namespace TwitchDesktopNotifications.Core
myProcess.StartInfo.UseShellExecute = true;
myProcess.StartInfo.FileName = args["streamerUrl"];
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)
{
String FilePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "TwitchNotify");
@@ -60,52 +56,64 @@ namespace TwitchDesktopNotifications.Core
string fileNameProfilePic = profilePic.Split("/").Last();
if (!File.Exists(FilePath + "/" + fileNameProfilePic))
{
(new WebClient()).DownloadFile(new Uri(profilePic), FilePath + "/" + fileNameProfilePic);
webClient.DownloadFile(new Uri(profilePic), FilePath + "/" + fileNameProfilePic);
}
// download there profile picture
string fileNameThumbnailPic = streamThumbnail.Split("/").Last();
(new WebClient()).DownloadFile(new Uri(streamThumbnail),
webClient.DownloadFile(new Uri(streamThumbnail),
FilePath + "/" + fileNameThumbnailPic
);
var builder = new ToastContentBuilder()
.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 =>
if (NotifyManager.ShouldNotify(streamerName))
{
toast.ExpirationTime = DateTime.Now.AddSeconds(15);
toast.Dismissed += (ToastNotification sender, ToastDismissedEventArgs args) =>
var builder = new ToastContentBuilder()
.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
{
File.Delete(FilePath + "/" + fileNameThumbnailPic);
}catch(Exception) { }
};
toast.Activated += (ToastNotification sender, object args) =>
builder.AddText(title);
}
builder.Show(toast =>
{
try
toast.ExpirationTime = DateTime.Now.AddSeconds(15);
toast.Dismissed += (ToastNotification sender, ToastDismissedEventArgs args) =>
{
File.Delete(FilePath + "/" + fileNameThumbnailPic);
}
catch (Exception) { }
};
});
try
{
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

@@ -0,0 +1,29 @@
using Microsoft.Toolkit.Uwp.Notifications;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Linq;
using TwitchDesktopNotifications.JsonStructure.Helix;
namespace TwitchDesktopNotifications.Core
{
internal class NotifyManager
{
public static Boolean ShouldNotify(String streamerName)
{
if(DataStore.GetInstance().Store.SteamersToIgnore == null || DataStore.GetInstance().Store.SteamersToIgnore.Streamers == null) {
return true;
}
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
{
internal class TwitcherRefreshException : Exception
{
public TwitcherRefreshException(string? message, Exception? innerException) : base(message, innerException) {
}
}
internal class 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; }
List <StreamsData> currentlyLive = null;
@@ -46,36 +65,55 @@ namespace TwitchDesktopNotifications.Core
{
throw new Exception("Not Authenticated");
}
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"] = TwitchDetails.TwitchClientID;
WebResponse response = request.GetResponse();
Stream dataStream = response.GetResponseStream();
StreamReader reader = new StreamReader(dataStream);
string responseFromServer = reader.ReadToEnd();
reader.Close();
dataStream.Close();
response.Close();
try
{
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"] = TwitchDetails.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);
return JsonSerializer.Deserialize<T>(responseFromServer);
}
catch (TwitcherRefreshException ex)
{
OpenFailedNotification();
}
catch(Exception ex)
{
Logger.GetInstance().Writer.WriteLineAsync(ex.ToString());
}
return default(T);
}
public void FetchCurrentUser()
{
try
{
DataStore.GetInstance().Store.UserData = MakeRequest<User>("helix/users").Data[0];
DataStore.GetInstance().Save();
}catch(System.Exception ex)
var UserList = MakeRequest<User>("helix/users");
if (UserList.Data.Count > 0) {
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
{
return MakeRequest<User>("helix/users?id=" + user_id).Data[0];
}catch(System.Exception ex)
var Response = MakeRequest<User>("helix/users?id=" + user_id);
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;
}
@@ -104,7 +151,7 @@ namespace TwitchDesktopNotifications.Core
string QueryUrl = "helix/streams/followed?first=100&user_id=" + DataStore.GetInstance().Store.UserData.UserId;
Streams following = MakeRequest<Streams>(QueryUrl);
if (currentlyLive != null)
if (following != null && currentlyLive != null)
{
following.Data.ForEach(x =>
{
@@ -118,48 +165,60 @@ namespace TwitchDesktopNotifications.Core
if (!found)
{
UserData streamer = FetchUserData(x.UserId);
UIStreamer.GetCreateStreamer(x.DisplayName);
Notification.GetInstance().sendNotification(streamer.DisplayName, "https://twitch.tv/" + streamer.UserName, streamer.ProfileImage, x.ThumbnailImg, x.Title);
}
});
}
currentlyLive = following.Data;
}catch(System.Exception ex)
{
MessageBox.Show("Twitch Connection not authenticated you need to Reconnect it.", "Twitch Notify");
}
}
catch (TwitcherRefreshException ex)
{
OpenFailedNotification();
}
catch (Exception ex)
{
Logger.GetInstance().Writer.WriteLineAsync(ex.ToString());
}
}
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_secret"] = TwitchDetails.TwitchClientSecret;
postData["grant_type"] = "refresh_token";
postData["refresh_token"] = DataStore.GetInstance().Store.Authentication.RefreshToken;
postData["client_id"] = TwitchDetails.TwitchClientID;
postData["client_secret"] = TwitchDetails.TwitchClientSecret;
postData["grant_type"] = "refresh_token";
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");
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();
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();
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();
}catch(Exception e)
{
throw new TwitcherRefreshException("Unable to refresh", e);
}
}
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
{
internal class WebServer
public class WebServer : SingletonFactory<WebServer>, Singleton
{
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; }

View File

@@ -1,14 +1,12 @@
using System.Text.Json;
using TwitchDesktopNotifications.JsonStructure;
using System.IO;
using TwitchDesktopNotifications.Core;
namespace TwitchDesktopNotifications
{
internal class DataStore
public class DataStore : SingletonFactory<DataStore>, Singleton
{
private DataStore() { }
public static DataStore Instance { get; private set; }
private Store _store;
public JsonStructure.Store Store {
get {
@@ -25,15 +23,6 @@ namespace TwitchDesktopNotifications
public bool isLoaded { get; private set; }
public static DataStore GetInstance()
{
if(Instance == null)
{
Instance = new DataStore();
}
return Instance;
}
public void Save()
{
String FilePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "TwitchNotify");
@@ -41,29 +30,30 @@ namespace TwitchDesktopNotifications
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);
File.WriteAllText(FilePath + "/" + FileName, fileContent);
}
public void Load() {
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
if (!isLoaded)
{
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
{
internal class Authentication
public class Authentication
{
[JsonPropertyName("access_token")]
public string AccessToken { get; set; }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -7,7 +7,7 @@ using System.Threading.Tasks;
namespace TwitchDesktopNotifications.JsonStructure.Helix
{
internal class UsersFollowsData
public class 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

@@ -8,14 +8,28 @@ using TwitchDesktopNotifications.JsonStructure.Helix;
namespace TwitchDesktopNotifications.JsonStructure
{
internal class Store
public class Store
{
public Store() { }
[JsonPropertyName("ignore")]
public SteamersToIgnore ignore { get; set; }
[JsonPropertyName("authentication")]
public Authentication Authentication { get; set; }
[JsonPropertyName("user_data")]
public UserData UserData { get; set; }
[JsonIgnore]
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="CanResizeWithGrip">
<Window.DataContext>
<core:UIStreamer />
</Window.DataContext>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="10" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="10" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="10" />
<RowDefinition Height="30" />
<RowDefinition Height="10" />
<RowDefinition Height="*" />
<RowDefinition Height="10" />
<RowDefinition Height="25" />
<RowDefinition Height="10" />
</Grid.RowDefinitions>
<DataGrid
Grid.Column="1"
Grid.RowSpan="1" Grid.Row="3"
CanUserResizeColumns="False"
x:Name="dgrdIgnore" ItemsSource="{Binding StreamersToIgnore}" SelectionChanged="dgrdIgnore_SelectionChanged" AutoGenerateColumns="False" CanUserAddRows="False">
<DataGrid.Columns>
<DataGridCheckBoxColumn Header="Ignore" Binding="{Binding IsIgnored}" IsReadOnly="False" Width="50">
</DataGridCheckBoxColumn>
<DataGridTextColumn Header="Streamer Name" Binding="{Binding Name}" IsReadOnly="True" />
<DataGridHyperlinkColumn Header="Streamer Link" Binding="{Binding Link}" Width="*">
<DataGridHyperlinkColumn.ElementStyle>
<Style TargetType="TextBlock">
<EventSetter Event="Hyperlink.Click" Handler="HyperLink_Click"/>
</Style>
</DataGridHyperlinkColumn.ElementStyle>
</DataGridHyperlinkColumn>
</DataGrid.Columns>
</DataGrid>
<Button Content="Close" Grid.Column="1" VerticalAlignment="Center" HorizontalAlignment="Right" Width="100" Grid.Row="5" Click="CloseBtn_Click" />
<TextBlock Grid.Column="1" Grid.Row="1" TextWrapping="Wrap" Text="Changes to the ignore list are automatically saved." VerticalAlignment="Top"/>
<TextBlock Grid.Column="1" Grid.Row="1" 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,45 @@
using System.Diagnostics;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Forms;
using System.Windows.Threading;
using TwitchDesktopNotifications.Core;
namespace TwitchDesktopNotifications
{
/// <summary>
/// Interaction logic for ManageIgnores.xaml
/// </summary>
public partial class ManageIgnores : Window
{
public ManageIgnores()
{
InitializeComponent();
}
List<UIStreamer> StreamersToIgnore = DataStore.GetInstance().Store.SteamersToIgnore.Streamers;
private void CloseBtn_Click(object sender, RoutedEventArgs e)
{
this.Close();
}
private void dgrdIgnore_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
Dispatcher.BeginInvoke(DispatcherPriority.Render, new Action(() => dgrdIgnore.UnselectAll()));
}
private void HyperLink_Click(object sender, RoutedEventArgs e)
{
string link = ((Hyperlink)e.OriginalSource).NavigateUri.OriginalString;
var psi = new ProcessStartInfo
{
FileName = link,
UseShellExecute = true
};
Process.Start(psi);
}
}
}

View File

@@ -18,6 +18,7 @@ internal class Program
private static NotifyIcon notifyIcon;
private static ContextMenuStrip cms;
private static ManageIgnores manageIgnores;
public static void Ws_CodeRecived(object? sender, EventArgs e)
{
@@ -45,6 +46,21 @@ internal class Program
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)
{
notifyIcon.Visible = false;
@@ -62,12 +78,13 @@ internal class Program
{
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
{
@@ -78,7 +95,8 @@ internal class Program
notifyIcon.Text = "Twitch Notify";
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 ToolStripSeparator());
cms.Items.Add(new ToolStripMenuItem("Quit", null, new EventHandler(Quit_Click), "Quit"));
@@ -91,7 +109,7 @@ internal class Program
TriggerAuthentication();
}
new Thread(() =>
Thread thread = new Thread(() =>
{
while (true)
{
@@ -101,12 +119,14 @@ internal class Program
TwitchFetcher.GetInstance().GetLiveFollowingUsers();
}
}
}).Start();
});
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
Application.Run();
}
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>