A 'little' code cleanup
This commit is contained in:
parent
a8a481d4f9
commit
8a10573fbf
168
App.axaml.cs
168
App.axaml.cs
@ -1,6 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.IO;
|
|
||||||
using System.Linq;
|
|
||||||
using Avalonia;
|
using Avalonia;
|
||||||
using Avalonia.Controls.ApplicationLifetimes;
|
using Avalonia.Controls.ApplicationLifetimes;
|
||||||
using Avalonia.Data.Core.Plugins;
|
using Avalonia.Data.Core.Plugins;
|
||||||
@ -11,180 +9,30 @@ using BetterRaid.Services.Implementations;
|
|||||||
using BetterRaid.ViewModels;
|
using BetterRaid.ViewModels;
|
||||||
using BetterRaid.Views;
|
using BetterRaid.Views;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using TwitchLib.Api;
|
|
||||||
using TwitchLib.PubSub;
|
|
||||||
|
|
||||||
namespace BetterRaid;
|
namespace BetterRaid;
|
||||||
|
|
||||||
public partial class App : Application
|
public partial class App : Application
|
||||||
{
|
{
|
||||||
private readonly ServiceCollection _services = [];
|
private static readonly ServiceCollection Services = [];
|
||||||
private ServiceProvider? _provider;
|
private static ServiceProvider? _serviceProvider;
|
||||||
|
|
||||||
private static TwitchAPI? _twitchApi;
|
public static IServiceProvider? ServiceProvider => _serviceProvider;
|
||||||
private static bool _hasUserZnSubbed;
|
|
||||||
private static string _betterRaidDataPath = "";
|
|
||||||
private static string _twitchBroadcasterId = "";
|
|
||||||
private static string _twitchOAuthAccessToken = "";
|
|
||||||
private static string _twitchOAuthAccessTokenFilePath = "";
|
|
||||||
private const string TokenClientId = "kkxu4jorjrrc5jch1ito5i61hbev2o";
|
|
||||||
private const string TwitchOAuthRedirectUrl = "http://localhost:9900";
|
|
||||||
private const string TwitchOAuthResponseType = "token";
|
|
||||||
|
|
||||||
private static readonly string[] TwitchOAuthScopes = [
|
|
||||||
"channel:manage:raids",
|
|
||||||
"user:read:subscriptions"
|
|
||||||
];
|
|
||||||
|
|
||||||
internal static readonly string TwitchOAuthUrl = $"https://id.twitch.tv/oauth2/authorize"
|
|
||||||
+ $"?client_id={TokenClientId}"
|
|
||||||
+ $"&redirect_uri={TwitchOAuthRedirectUrl}"
|
|
||||||
+ $"&response_type={TwitchOAuthResponseType}"
|
|
||||||
+ $"&scope={string.Join("+", TwitchOAuthScopes)}";
|
|
||||||
|
|
||||||
public const string ChannelPlaceholderImageUrl = "https://cdn.pixabay.com/photo/2018/11/13/22/01/avatar-3814081_1280.png";
|
|
||||||
|
|
||||||
public static TwitchAPI? TwitchApi => _twitchApi;
|
|
||||||
public static bool HasUserZnSubbed => _hasUserZnSubbed;
|
|
||||||
public static string BetterRaidDataPath => _betterRaidDataPath;
|
|
||||||
|
|
||||||
public IServiceProvider? Provider => _provider;
|
|
||||||
public static string? TwitchBroadcasterId => _twitchBroadcasterId;
|
|
||||||
|
|
||||||
public static string TwitchOAuthAccessToken
|
|
||||||
{
|
|
||||||
get => _twitchOAuthAccessToken;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
_twitchOAuthAccessToken = value;
|
|
||||||
InitTwitchClient(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
LoadTwitchToken();
|
|
||||||
InitializeServices();
|
InitializeServices();
|
||||||
|
|
||||||
AvaloniaXamlLoader.Load(_provider, this);
|
AvaloniaXamlLoader.Load(_serviceProvider, this);
|
||||||
}
|
|
||||||
|
|
||||||
private void LoadTwitchToken()
|
|
||||||
{
|
|
||||||
var userHomeDir = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
|
|
||||||
|
|
||||||
_betterRaidDataPath = Environment.OSVersion.Platform switch
|
|
||||||
{
|
|
||||||
PlatformID.Win32NT => Path.Combine(userHomeDir, "AppData", "Roaming", "BetterRaid"),
|
|
||||||
PlatformID.Unix => Path.Combine(userHomeDir, ".config", "BetterRaid"),
|
|
||||||
PlatformID.MacOSX => Path.Combine(userHomeDir, "Library", "Application Support", "BetterRaid"),
|
|
||||||
_ => throw new PlatformNotSupportedException($"Your platform '{Environment.OSVersion.Platform}' is not supported. Please report this issue here: https://www.github.com/zion-networks/BetterRaid/issues")
|
|
||||||
};
|
|
||||||
|
|
||||||
if (!Directory.Exists(_betterRaidDataPath))
|
|
||||||
{
|
|
||||||
var di = Directory.CreateDirectory(_betterRaidDataPath);
|
|
||||||
if (di.Exists == false)
|
|
||||||
{
|
|
||||||
throw new Exception($"Failed to create directory '{_betterRaidDataPath}'");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_twitchOAuthAccessTokenFilePath = Path.Combine(_betterRaidDataPath, ".access_token");
|
|
||||||
|
|
||||||
if (!File.Exists(_twitchOAuthAccessTokenFilePath))
|
|
||||||
return;
|
|
||||||
|
|
||||||
_twitchOAuthAccessToken = File.ReadAllText(_twitchOAuthAccessTokenFilePath);
|
|
||||||
InitTwitchClient();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void InitializeServices()
|
private void InitializeServices()
|
||||||
{
|
{
|
||||||
_services.AddSingleton<ITwitchDataService, TwitchDataService>();
|
Services.AddSingleton<ITwitchDataService, TwitchDataService>();
|
||||||
_services.AddSingleton<ITwitchPubSubService, TwitchPubSubService>();
|
Services.AddSingleton<ITwitchPubSubService, TwitchPubSubService>();
|
||||||
_services.AddTransient<MainWindowViewModel>();
|
Services.AddTransient<MainWindowViewModel>();
|
||||||
|
|
||||||
_provider = _services.BuildServiceProvider();
|
_serviceProvider = Services.BuildServiceProvider();
|
||||||
}
|
|
||||||
|
|
||||||
public static void InitTwitchClient(bool overrideToken = false)
|
|
||||||
{
|
|
||||||
Console.WriteLine("[INFO] Initializing Twitch Client...");
|
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(_twitchOAuthAccessToken))
|
|
||||||
{
|
|
||||||
Console.WriteLine("[ERROR] Failed to initialize Twitch Client: Access Token is empty!");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
_twitchApi = new TwitchAPI
|
|
||||||
{
|
|
||||||
Settings =
|
|
||||||
{
|
|
||||||
ClientId = TokenClientId,
|
|
||||||
AccessToken = _twitchOAuthAccessToken
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Console.WriteLine("[INFO] Testing Twitch API connection...");
|
|
||||||
|
|
||||||
var user = _twitchApi.Helix.Users.GetUsersAsync().Result.Users.FirstOrDefault();
|
|
||||||
if (user == null)
|
|
||||||
{
|
|
||||||
_twitchApi = null;
|
|
||||||
Console.WriteLine("[ERROR] Failed to connect to Twitch API!");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var channel = _twitchApi.Helix.Search
|
|
||||||
.SearchChannelsAsync(user.Login).Result.Channels
|
|
||||||
.FirstOrDefault(c => c.BroadcasterLogin == user.Login);
|
|
||||||
|
|
||||||
var userSubs = _twitchApi.Helix.Subscriptions.CheckUserSubscriptionAsync(
|
|
||||||
userId: user.Id,
|
|
||||||
broadcasterId: "1120558409"
|
|
||||||
).Result.Data;
|
|
||||||
|
|
||||||
if (userSubs is { Length: > 0 } && userSubs.Any(s => s.BroadcasterId == "1120558409"))
|
|
||||||
{
|
|
||||||
_hasUserZnSubbed = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (channel == null)
|
|
||||||
{
|
|
||||||
Console.WriteLine($"[ERROR] Failed to get channel information for '{user.Login}'!");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
_twitchBroadcasterId = channel.Id;
|
|
||||||
|
|
||||||
Console.WriteLine("[INFO] Connected to Twitch API as '{0}'!", user.DisplayName);
|
|
||||||
|
|
||||||
if (!overrideToken)
|
|
||||||
return;
|
|
||||||
|
|
||||||
File.WriteAllText(_twitchOAuthAccessTokenFilePath, _twitchOAuthAccessToken);
|
|
||||||
|
|
||||||
switch (Environment.OSVersion.Platform)
|
|
||||||
{
|
|
||||||
case PlatformID.Win32NT:
|
|
||||||
File.SetAttributes(_twitchOAuthAccessTokenFilePath, File.GetAttributes(_twitchOAuthAccessTokenFilePath) | FileAttributes.Hidden);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case PlatformID.Unix:
|
|
||||||
#pragma warning disable CA1416 // Validate platform compatibility
|
|
||||||
File.SetUnixFileMode(_twitchOAuthAccessTokenFilePath, UnixFileMode.UserRead);
|
|
||||||
#pragma warning restore CA1416 // Validate platform compatibility
|
|
||||||
break;
|
|
||||||
|
|
||||||
case PlatformID.MacOSX:
|
|
||||||
File.SetAttributes(_twitchOAuthAccessTokenFilePath, File.GetAttributes(_twitchOAuthAccessTokenFilePath) | FileAttributes.Hidden);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
throw new PlatformNotSupportedException($"Your platform '{Environment.OSVersion.Platform}' is not supported. Please report this issue here: https://www.github.com/zion-networks/BetterRaid/issues");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void OnFrameworkInitializationCompleted()
|
public override void OnFrameworkInitializationCompleted()
|
||||||
|
@ -1,95 +0,0 @@
|
|||||||
<UserControl xmlns="https://github.com/avaloniaui"
|
|
||||||
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:ai="using:AsyncImageLoader"
|
|
||||||
xmlns:vm="using:BetterRaid.ViewModels"
|
|
||||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
|
||||||
x:Class="BetterRaid.RaidButton"
|
|
||||||
x:DataType="vm:RaidButtonViewModel"
|
|
||||||
Margin="5"
|
|
||||||
HorizontalAlignment="Stretch"
|
|
||||||
VerticalAlignment="Stretch">
|
|
||||||
|
|
||||||
<Design.DataContext>
|
|
||||||
<vm:RaidButtonViewModel />
|
|
||||||
</Design.DataContext>
|
|
||||||
|
|
||||||
<Button HorizontalAlignment="Stretch"
|
|
||||||
VerticalAlignment="Stretch"
|
|
||||||
Command="{Binding RaidChannel}">
|
|
||||||
|
|
||||||
<Grid HorizontalAlignment="Stretch"
|
|
||||||
VerticalAlignment="Stretch">
|
|
||||||
|
|
||||||
<Grid.ColumnDefinitions>
|
|
||||||
<ColumnDefinition Width="*" />
|
|
||||||
</Grid.ColumnDefinitions>
|
|
||||||
|
|
||||||
<Grid.RowDefinitions>
|
|
||||||
<RowDefinition Height="75*" />
|
|
||||||
<RowDefinition Height="25*" />
|
|
||||||
</Grid.RowDefinitions>
|
|
||||||
|
|
||||||
<ai:AdvancedImage Grid.Column="0"
|
|
||||||
Grid.Row="0"
|
|
||||||
Source="{Binding Channel.ThumbnailUrl}" />
|
|
||||||
|
|
||||||
<Border IsVisible="{Binding IsAd}"
|
|
||||||
BorderThickness="1"
|
|
||||||
BorderBrush="DarkGoldenrod"
|
|
||||||
CornerRadius="4"
|
|
||||||
Width="24"
|
|
||||||
Height="16"
|
|
||||||
HorizontalAlignment="Left"
|
|
||||||
VerticalAlignment="Top"
|
|
||||||
Margin="5">
|
|
||||||
<TextBlock Text="Ad"
|
|
||||||
Margin="2"
|
|
||||||
FontSize="12"
|
|
||||||
HorizontalAlignment="Center"
|
|
||||||
VerticalAlignment="Center"
|
|
||||||
TextAlignment="Center"
|
|
||||||
Foreground="DarkGoldenrod" />
|
|
||||||
</Border>
|
|
||||||
|
|
||||||
|
|
||||||
<Button Width="32"
|
|
||||||
Height="32"
|
|
||||||
Background="DarkRed"
|
|
||||||
CornerRadius="16"
|
|
||||||
Padding="0"
|
|
||||||
BorderThickness="0"
|
|
||||||
HorizontalContentAlignment="Center"
|
|
||||||
VerticalContentAlignment="Center"
|
|
||||||
HorizontalAlignment="Right"
|
|
||||||
VerticalAlignment="Top"
|
|
||||||
Margin="5"
|
|
||||||
IsVisible="{Binding !HideDeleteButton}"
|
|
||||||
Command="{Binding RemoveChannel}">
|
|
||||||
<Image Source="avares://BetterRaid/Assets/icons8-close-32.png"
|
|
||||||
HorizontalAlignment="Stretch"
|
|
||||||
VerticalAlignment="Stretch" />
|
|
||||||
</Button>
|
|
||||||
|
|
||||||
<StackPanel Grid.Column="0"
|
|
||||||
Grid.Row="1"
|
|
||||||
Orientation="Vertical">
|
|
||||||
<Label HorizontalAlignment="Stretch"
|
|
||||||
HorizontalContentAlignment="Center"
|
|
||||||
Content="{Binding Channel.DisplayName, FallbackValue=...}" />
|
|
||||||
<Label HorizontalAlignment="Stretch"
|
|
||||||
HorizontalContentAlignment="Center"
|
|
||||||
Content="{Binding Channel.Category, TargetNullValue=-, FallbackValue=...}" />
|
|
||||||
<Label HorizontalAlignment="Stretch"
|
|
||||||
HorizontalContentAlignment="Center"
|
|
||||||
Foreground="{Binding ViewerCountColor}"
|
|
||||||
Content="{Binding Channel.ViewerCount, TargetNullValue=(Offline), FallbackValue=...}" />
|
|
||||||
<Label HorizontalAlignment="Stretch"
|
|
||||||
HorizontalContentAlignment="Center"
|
|
||||||
Content="{Binding LastRaided, TargetNullValue=Never Raided, FallbackValue=...}" />
|
|
||||||
</StackPanel>
|
|
||||||
|
|
||||||
</Grid>
|
|
||||||
</Button>
|
|
||||||
</UserControl>
|
|
@ -1,13 +0,0 @@
|
|||||||
using Avalonia;
|
|
||||||
using Avalonia.Controls;
|
|
||||||
using Avalonia.Markup.Xaml;
|
|
||||||
|
|
||||||
namespace BetterRaid;
|
|
||||||
|
|
||||||
public partial class RaidButton : UserControl
|
|
||||||
{
|
|
||||||
public RaidButton()
|
|
||||||
{
|
|
||||||
InitializeComponent();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,5 +1,4 @@
|
|||||||
using Avalonia;
|
using Avalonia;
|
||||||
using Avalonia.Controls;
|
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
|
||||||
namespace BetterRaid.Extensions;
|
namespace BetterRaid.Extensions;
|
||||||
@ -13,10 +12,10 @@ public static class DataContextExtensions
|
|||||||
|
|
||||||
public static void InjectDataContext<T>(this StyledElement e) where T : class
|
public static void InjectDataContext<T>(this StyledElement e) where T : class
|
||||||
{
|
{
|
||||||
if (Application.Current is not App { Provider: not null } app)
|
if (App.ServiceProvider == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var vm = app.Provider.GetRequiredService<T>();
|
var vm = App.ServiceProvider.GetRequiredService<T>();
|
||||||
e.DataContext = vm;
|
e.DataContext = vm;
|
||||||
}
|
}
|
||||||
}
|
}
|
30
Misc/Constants.cs
Normal file
30
Misc/Constants.cs
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
|
namespace BetterRaid.Misc;
|
||||||
|
|
||||||
|
public class Constants
|
||||||
|
{
|
||||||
|
// General
|
||||||
|
public const string ChannelPlaceholderImageUrl = "https://cdn.pixabay.com/photo/2018/11/13/22/01/avatar-3814081_1280.png";
|
||||||
|
|
||||||
|
// Paths
|
||||||
|
public static string BetterRaidDataPath => Environment.OSVersion.Platform switch
|
||||||
|
{
|
||||||
|
PlatformID.Win32NT => Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), "AppData", "Roaming", "BetterRaid"),
|
||||||
|
PlatformID.Unix => Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".config", "BetterRaid"),
|
||||||
|
PlatformID.MacOSX => Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), "Library", "Application Support", "BetterRaid"),
|
||||||
|
_ => throw new PlatformNotSupportedException($"Your platform '{Environment.OSVersion.Platform}' is not supported. Please report this issue here: https://www.github.com/zion-networks/BetterRaid/issues")
|
||||||
|
};
|
||||||
|
public static string TwitchOAuthAccessTokenFilePath => Path.Combine(BetterRaidDataPath, ".access_token");
|
||||||
|
public static string DatabaseFilePath => Path.Combine(BetterRaidDataPath, "db.json");
|
||||||
|
|
||||||
|
// Twitch API
|
||||||
|
public const string TwitchClientId = "kkxu4jorjrrc5jch1ito5i61hbev2o";
|
||||||
|
public const string TwitchOAuthRedirectUrl = "http://localhost:9900";
|
||||||
|
public const string TwitchOAuthResponseType = "token";
|
||||||
|
public static readonly string[] TwitchOAuthScopes = [
|
||||||
|
"channel:manage:raids", // Allows the application to start and cancel raids on the broadcaster's channel
|
||||||
|
"user:read:subscriptions" // Allows the application to check, if the user has subscribed to the developer's channel
|
||||||
|
];
|
||||||
|
}
|
@ -6,13 +6,13 @@ using System.Text;
|
|||||||
using System.Text.Json.Nodes;
|
using System.Text.Json.Nodes;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using BetterRaid.Services;
|
||||||
|
|
||||||
namespace BetterRaid.Misc;
|
namespace BetterRaid.Misc;
|
||||||
|
|
||||||
public static class Tools
|
public static class Tools
|
||||||
{
|
{
|
||||||
private static HttpListener? _oauthListener;
|
private static HttpListener? _oauthListener;
|
||||||
private static Task? _oauthWaiterTask;
|
|
||||||
|
|
||||||
// Source: https://stackoverflow.com/a/43232486
|
// Source: https://stackoverflow.com/a/43232486
|
||||||
public static void StartOAuthLogin(string url, Action? callback = null, CancellationToken token = default)
|
public static void StartOAuthLogin(string url, Action? callback = null, CancellationToken token = default)
|
||||||
@ -20,10 +20,10 @@ public static class Tools
|
|||||||
if (_oauthListener == null)
|
if (_oauthListener == null)
|
||||||
{
|
{
|
||||||
_oauthListener = new HttpListener();
|
_oauthListener = new HttpListener();
|
||||||
_oauthListener.Prefixes.Add("http://localhost:9900/");
|
_oauthListener.Prefixes.Add(Constants.TwitchOAuthRedirectUrl);
|
||||||
_oauthListener.Start();
|
_oauthListener.Start();
|
||||||
|
|
||||||
_oauthWaiterTask = WaitForCallback(callback, token);
|
Task.Run(() => WaitForCallback(callback, token), token);
|
||||||
}
|
}
|
||||||
|
|
||||||
OpenUrl(url);
|
OpenUrl(url);
|
||||||
@ -84,7 +84,7 @@ public static class Tools
|
|||||||
req.InputStream.Close();
|
req.InputStream.Close();
|
||||||
|
|
||||||
var json = data.ToString();
|
var json = data.ToString();
|
||||||
var jsonData = JsonObject.Parse(json);
|
var jsonData = JsonNode.Parse(json);
|
||||||
|
|
||||||
if (jsonData == null)
|
if (jsonData == null)
|
||||||
{
|
{
|
||||||
@ -106,7 +106,12 @@ public static class Tools
|
|||||||
}
|
}
|
||||||
|
|
||||||
var accessToken = jsonData["access_token"]?.ToString();
|
var accessToken = jsonData["access_token"]?.ToString();
|
||||||
App.TwitchOAuthAccessToken = accessToken!;
|
|
||||||
|
var dataService = App.ServiceProvider?.GetService(typeof(ITwitchDataService));
|
||||||
|
if (dataService is ITwitchDataService twitchDataService)
|
||||||
|
{
|
||||||
|
twitchDataService.ConnectApi(Constants.TwitchClientId, accessToken!);
|
||||||
|
}
|
||||||
|
|
||||||
res.StatusCode = 200;
|
res.StatusCode = 200;
|
||||||
res.Close();
|
res.Close();
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using BetterRaid.Attributes;
|
using BetterRaid.Attributes;
|
||||||
|
using BetterRaid.Services;
|
||||||
|
|
||||||
namespace BetterRaid.Models;
|
namespace BetterRaid.Models;
|
||||||
|
|
||||||
@ -142,15 +142,15 @@ public class TwitchChannel : INotifyPropertyChanged
|
|||||||
Name = channelName;
|
Name = channelName;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void InitChannel()
|
public void UpdateChannelData(ITwitchDataService dataService)
|
||||||
{
|
{
|
||||||
var channel = App.TwitchApi?.Helix.Search.SearchChannelsAsync(Name).Result.Channels
|
var channel = dataService.TwitchApi.Helix.Search.SearchChannelsAsync(Name).Result.Channels
|
||||||
.FirstOrDefault(c => c.BroadcasterLogin.Equals(Name, StringComparison.CurrentCultureIgnoreCase));
|
.FirstOrDefault(c => c.BroadcasterLogin.Equals(Name, StringComparison.CurrentCultureIgnoreCase));
|
||||||
|
|
||||||
if (channel == null)
|
if (channel == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var stream = App.TwitchApi?.Helix.Streams.GetStreamsAsync(userLogins: [ Name ]).Result.Streams
|
var stream = dataService.TwitchApi.Helix.Streams.GetStreamsAsync(userLogins: [ Name ]).Result.Streams
|
||||||
.FirstOrDefault(s => s.UserLogin.Equals(Name, StringComparison.CurrentCultureIgnoreCase));
|
.FirstOrDefault(s => s.UserLogin.Equals(Name, StringComparison.CurrentCultureIgnoreCase));
|
||||||
|
|
||||||
BroadcasterId = channel.Id;
|
BroadcasterId = channel.Id;
|
||||||
|
@ -1,12 +1,26 @@
|
|||||||
|
using System.ComponentModel;
|
||||||
|
using BetterRaid.Models;
|
||||||
|
using TwitchLib.Api;
|
||||||
|
|
||||||
namespace BetterRaid.Services;
|
namespace BetterRaid.Services;
|
||||||
|
|
||||||
public interface ITwitchDataService
|
public interface ITwitchDataService
|
||||||
{
|
{
|
||||||
|
public string? AccessToken { get; set; }
|
||||||
|
public TwitchChannel? UserChannel { get; set; }
|
||||||
|
public TwitchAPI TwitchApi { get; }
|
||||||
public bool IsRaidStarted { get; set; }
|
public bool IsRaidStarted { get; set; }
|
||||||
|
|
||||||
|
public void ConnectApi(string clientId, string accessToken);
|
||||||
|
public void SaveAccessToken(string token);
|
||||||
|
public bool TryGetUserChannel(out TwitchChannel? userChannel);
|
||||||
|
public string GetOAuthUrl();
|
||||||
public void StartRaid(string from, string to);
|
public void StartRaid(string from, string to);
|
||||||
public void StartRaidCommand(object? arg);
|
public void StartRaidCommand(object? arg);
|
||||||
public void StopRaid();
|
public void StopRaid();
|
||||||
public void StopRaidCommand();
|
public void StopRaidCommand();
|
||||||
public void OpenChannelCommand(object? arg);
|
public void OpenChannelCommand(object? arg);
|
||||||
|
|
||||||
|
public event PropertyChangingEventHandler? PropertyChanging;
|
||||||
|
public event PropertyChangedEventHandler? PropertyChanged;
|
||||||
}
|
}
|
@ -1,36 +1,142 @@
|
|||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
|
using System.IO;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using BetterRaid.Misc;
|
using BetterRaid.Misc;
|
||||||
|
using BetterRaid.Models;
|
||||||
|
using TwitchLib.Api;
|
||||||
|
|
||||||
namespace BetterRaid.Services.Implementations;
|
namespace BetterRaid.Services.Implementations;
|
||||||
|
|
||||||
public class TwitchDataService : ITwitchDataService, INotifyPropertyChanged
|
public class TwitchDataService : ITwitchDataService, INotifyPropertyChanged, INotifyPropertyChanging
|
||||||
{
|
{
|
||||||
private bool _isRaidStarted;
|
private bool _isRaidStarted;
|
||||||
|
private TwitchChannel? _userChannel;
|
||||||
|
|
||||||
|
public string AccessToken { get; set; } = string.Empty;
|
||||||
|
|
||||||
public bool IsRaidStarted
|
public bool IsRaidStarted
|
||||||
{
|
{
|
||||||
get => _isRaidStarted;
|
get => _isRaidStarted;
|
||||||
set => SetField(ref _isRaidStarted, value);
|
set => SetField(ref _isRaidStarted, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public TwitchChannel? UserChannel
|
||||||
|
{
|
||||||
|
get => _userChannel;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (_userChannel != null && _userChannel.Name?.Equals(value?.Name) == true)
|
||||||
|
return;
|
||||||
|
|
||||||
|
SetField(ref _userChannel, value);
|
||||||
|
|
||||||
|
_userChannel?.UpdateChannelData(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public TwitchAPI TwitchApi { get; }
|
||||||
|
|
||||||
|
public TwitchDataService()
|
||||||
|
{
|
||||||
|
TwitchApi = new TwitchAPI();
|
||||||
|
|
||||||
|
if (TryLoadAccessToken(out var token))
|
||||||
|
{
|
||||||
|
Console.WriteLine($"[INFO][{nameof(TwitchDataService)}] Found access token.");
|
||||||
|
ConnectApi(Constants.TwitchClientId, token);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Console.WriteLine($"[INFO][{nameof(TwitchDataService)}] No access token found.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ConnectApi(string clientId, string accessToken)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"[INFO][{nameof(TwitchDataService)}] Connecting to Twitch API ...");
|
||||||
|
|
||||||
|
AccessToken = accessToken;
|
||||||
|
|
||||||
|
TwitchApi.Settings.ClientId = clientId;
|
||||||
|
TwitchApi.Settings.AccessToken = accessToken;
|
||||||
|
|
||||||
|
if (TryGetUserChannel(out var channel))
|
||||||
|
{
|
||||||
|
UserChannel = channel;
|
||||||
|
Console.WriteLine($"[INFO][{nameof(TwitchDataService)}] Connected to Twitch API as {channel?.Name}.");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
UserChannel = null;
|
||||||
|
|
||||||
|
Console.WriteLine($"[ERROR][{nameof(TwitchDataService)}] Could not get user channel.");
|
||||||
|
Console.WriteLine($"[ERROR][{nameof(TwitchDataService)}] Failed to connect to Twitch API.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool TryLoadAccessToken(out string token)
|
||||||
|
{
|
||||||
|
token = string.Empty;
|
||||||
|
|
||||||
|
if (!File.Exists(Constants.TwitchOAuthAccessTokenFilePath))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
token = File.ReadAllText(Constants.TwitchOAuthAccessTokenFilePath);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SaveAccessToken(string token)
|
||||||
|
{
|
||||||
|
File.WriteAllText(Constants.TwitchOAuthAccessTokenFilePath, token);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TryGetUserChannel(out TwitchChannel? userChannel)
|
||||||
|
{
|
||||||
|
userChannel = null;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var user = TwitchApi.Helix.Users.GetUsersAsync().Result.Users[0];
|
||||||
|
userChannel = new TwitchChannel(user.Login);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"[ERROR][{nameof(TwitchDataService)}] {e.Message}");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public string GetOAuthUrl()
|
||||||
|
{
|
||||||
|
var scopes = string.Join("+", Constants.TwitchOAuthScopes);
|
||||||
|
|
||||||
|
return $"https://id.twitch.tv/oauth2/authorize"
|
||||||
|
+ $"?client_id={Constants.TwitchClientId}"
|
||||||
|
+ $"&redirect_uri={Constants.TwitchOAuthRedirectUrl}"
|
||||||
|
+ $"&response_type={Constants.TwitchOAuthResponseType}"
|
||||||
|
+ $"&scope={scopes}";
|
||||||
|
}
|
||||||
|
|
||||||
public void StartRaid(string from, string to)
|
public void StartRaid(string from, string to)
|
||||||
{
|
{
|
||||||
// TODO: Also check, if the logged in user is live
|
// TODO: Also check, if the logged in user is live
|
||||||
|
|
||||||
App.TwitchApi?.Helix.Raids.StartRaidAsync(from, to);
|
TwitchApi.Helix.Raids.StartRaidAsync(from, to);
|
||||||
IsRaidStarted = true;
|
IsRaidStarted = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void StartRaidCommand(object? arg)
|
public void StartRaidCommand(object? arg)
|
||||||
{
|
{
|
||||||
if (arg == null || App.TwitchBroadcasterId == null)
|
if (arg == null || UserChannel?.BroadcasterId == null)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var from = App.TwitchBroadcasterId;
|
var from = UserChannel.BroadcasterId!;
|
||||||
var to = arg.ToString()!;
|
var to = arg.ToString()!;
|
||||||
|
|
||||||
StartRaid(from, to);
|
StartRaid(from, to);
|
||||||
@ -38,10 +144,13 @@ public class TwitchDataService : ITwitchDataService, INotifyPropertyChanged
|
|||||||
|
|
||||||
public void StopRaid()
|
public void StopRaid()
|
||||||
{
|
{
|
||||||
|
if (UserChannel?.BroadcasterId == null)
|
||||||
|
return;
|
||||||
|
|
||||||
if (IsRaidStarted == false)
|
if (IsRaidStarted == false)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
App.TwitchApi?.Helix.Raids.CancelRaidAsync(App.TwitchBroadcasterId);
|
TwitchApi.Helix.Raids.CancelRaidAsync(UserChannel.BroadcasterId);
|
||||||
IsRaidStarted = false;
|
IsRaidStarted = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -61,18 +170,29 @@ public class TwitchDataService : ITwitchDataService, INotifyPropertyChanged
|
|||||||
Tools.OpenUrl(url);
|
Tools.OpenUrl(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public event PropertyChangingEventHandler? PropertyChanging;
|
||||||
public event PropertyChangedEventHandler? PropertyChanged;
|
public event PropertyChangedEventHandler? PropertyChanged;
|
||||||
|
|
||||||
protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null)
|
protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null)
|
||||||
{
|
{
|
||||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected virtual void OnPropertyChanging([CallerMemberName] string? propertyName = null)
|
||||||
|
{
|
||||||
|
PropertyChanging?.Invoke(this, new PropertyChangingEventArgs(propertyName));
|
||||||
|
}
|
||||||
|
|
||||||
protected bool SetField<T>(ref T field, T value, [CallerMemberName] string? propertyName = null)
|
protected bool SetField<T>(ref T field, T value, [CallerMemberName] string? propertyName = null)
|
||||||
{
|
{
|
||||||
if (EqualityComparer<T>.Default.Equals(field, value)) return false;
|
if (EqualityComparer<T>.Default.Equals(field, value))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
OnPropertyChanging(propertyName);
|
||||||
field = value;
|
field = value;
|
||||||
OnPropertyChanged(propertyName);
|
OnPropertyChanged(propertyName);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -14,9 +14,12 @@ public class TwitchPubSubService : ITwitchPubSubService
|
|||||||
{
|
{
|
||||||
private readonly Dictionary<PubSubType, List<PubSubListener>> _targets = new();
|
private readonly Dictionary<PubSubType, List<PubSubListener>> _targets = new();
|
||||||
private readonly TwitchPubSub _sub;
|
private readonly TwitchPubSub _sub;
|
||||||
|
private readonly ITwitchDataService _dataService;
|
||||||
|
|
||||||
public TwitchPubSubService()
|
public TwitchPubSubService(ITwitchDataService dataService)
|
||||||
{
|
{
|
||||||
|
_dataService = dataService;
|
||||||
|
|
||||||
_sub = new TwitchPubSub();
|
_sub = new TwitchPubSub();
|
||||||
|
|
||||||
_sub.OnPubSubServiceConnected += OnSubOnOnPubSubServiceConnected;
|
_sub.OnPubSubServiceConnected += OnSubOnOnPubSubServiceConnected;
|
||||||
@ -26,6 +29,29 @@ public class TwitchPubSubService : ITwitchPubSubService
|
|||||||
_sub.OnViewCount += OnSubOnOnViewCount;
|
_sub.OnViewCount += OnSubOnOnViewCount;
|
||||||
|
|
||||||
_sub.Connect();
|
_sub.Connect();
|
||||||
|
|
||||||
|
if (_dataService.UserChannel != null)
|
||||||
|
{
|
||||||
|
RegisterReceiver(_dataService.UserChannel);
|
||||||
|
}
|
||||||
|
|
||||||
|
_dataService.PropertyChanging += (_, args) =>
|
||||||
|
{
|
||||||
|
if (args.PropertyName != nameof(_dataService.UserChannel))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (_dataService.UserChannel != null)
|
||||||
|
UnregisterReceiver(_dataService.UserChannel);
|
||||||
|
};
|
||||||
|
|
||||||
|
_dataService.PropertyChanged += (_, args) =>
|
||||||
|
{
|
||||||
|
if (args.PropertyName != nameof(_dataService.UserChannel))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (_dataService.UserChannel != null)
|
||||||
|
RegisterReceiver(_dataService.UserChannel);
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnSubOnOnViewCount(object? sender, OnViewCountArgs args)
|
private void OnSubOnOnViewCount(object? sender, OnViewCountArgs args)
|
||||||
@ -145,7 +171,7 @@ public class TwitchPubSubService : ITwitchPubSubService
|
|||||||
}
|
}
|
||||||
|
|
||||||
_sub.ListenToVideoPlayback(channelId);
|
_sub.ListenToVideoPlayback(channelId);
|
||||||
_sub.SendTopics(App.TwitchOAuthAccessToken);
|
_sub.SendTopics(_dataService.AccessToken);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -156,10 +182,9 @@ public class TwitchPubSubService : ITwitchPubSubService
|
|||||||
{
|
{
|
||||||
ArgumentNullException.ThrowIfNull(receiver, nameof(receiver));
|
ArgumentNullException.ThrowIfNull(receiver, nameof(receiver));
|
||||||
|
|
||||||
foreach (var target in _targets)
|
foreach (var (topic, listeners) in _targets)
|
||||||
{
|
{
|
||||||
var topic = target.Key;
|
var listener = listeners.Where(x => x.Instance == receiver).ToList();
|
||||||
var listener = target.Value.Where(x => x.Instance == receiver).ToList();
|
|
||||||
|
|
||||||
foreach (var l in listener)
|
foreach (var l in listener)
|
||||||
{
|
{
|
||||||
@ -177,7 +202,7 @@ public class TwitchPubSubService : ITwitchPubSubService
|
|||||||
break;
|
break;
|
||||||
case PubSubType.VideoPlayback:
|
case PubSubType.VideoPlayback:
|
||||||
_sub.ListenToVideoPlayback(l.ChannelId);
|
_sub.ListenToVideoPlayback(l.ChannelId);
|
||||||
_sub.SendTopics(App.TwitchOAuthAccessToken, true);
|
_sub.SendTopics(_dataService.AccessToken, true);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
using System.IO;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
@ -46,14 +45,14 @@ public partial class MainWindowViewModel : ViewModelBase
|
|||||||
set => SetProperty(ref _filter, value);
|
set => SetProperty(ref _filter, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool IsLoggedIn => App.TwitchApi != null;
|
public bool IsLoggedIn => DataService.UserChannel != null;
|
||||||
|
|
||||||
public MainWindowViewModel(ITwitchPubSubService pubSub, ITwitchDataService dataService)
|
public MainWindowViewModel(ITwitchPubSubService pubSub, ITwitchDataService dataService)
|
||||||
{
|
{
|
||||||
_pubSub = pubSub;
|
_pubSub = pubSub;
|
||||||
DataService = dataService;
|
DataService = dataService;
|
||||||
|
|
||||||
Database = BetterRaidDatabase.LoadFromFile(Path.Combine(App.BetterRaidDataPath, "db.json"));
|
Database = BetterRaidDatabase.LoadFromFile(Constants.DatabaseFilePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ExitApplication()
|
public void ExitApplication()
|
||||||
@ -71,7 +70,7 @@ public partial class MainWindowViewModel : ViewModelBase
|
|||||||
|
|
||||||
public void LoginWithTwitch()
|
public void LoginWithTwitch()
|
||||||
{
|
{
|
||||||
Tools.StartOAuthLogin(App.TwitchOAuthUrl, OnTwitchLoginCallback, CancellationToken.None);
|
Tools.StartOAuthLogin(DataService.GetOAuthUrl(), OnTwitchLoginCallback, CancellationToken.None);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnTwitchLoginCallback()
|
private void OnTwitchLoginCallback()
|
||||||
@ -101,7 +100,7 @@ public partial class MainWindowViewModel : ViewModelBase
|
|||||||
{
|
{
|
||||||
Task.Run(() =>
|
Task.Run(() =>
|
||||||
{
|
{
|
||||||
channel.InitChannel();
|
channel.UpdateChannelData(DataService);
|
||||||
_pubSub.RegisterReceiver(channel);
|
_pubSub.RegisterReceiver(channel);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1,195 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.ComponentModel;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Avalonia.Media;
|
|
||||||
using Avalonia.Threading;
|
|
||||||
using BetterRaid.Events;
|
|
||||||
using BetterRaid.Models;
|
|
||||||
using TwitchLib.Api.Helix.Models.Raids.StartRaid;
|
|
||||||
using TwitchLib.Api.Helix.Models.Search;
|
|
||||||
using TwitchLib.Api.Helix.Models.Streams.GetStreams;
|
|
||||||
|
|
||||||
namespace BetterRaid.ViewModels;
|
|
||||||
|
|
||||||
public class RaidButtonViewModel : ViewModelBase
|
|
||||||
{
|
|
||||||
private TwitchChannel? _channel;
|
|
||||||
private SolidColorBrush _viewerCountColor = new SolidColorBrush(Color.FromRgb(byte.MaxValue, byte.MaxValue, byte.MaxValue));
|
|
||||||
private bool _hideDeleteButton;
|
|
||||||
private bool _isAd;
|
|
||||||
|
|
||||||
public string ChannelName
|
|
||||||
{
|
|
||||||
get;
|
|
||||||
set;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool HideDeleteButton
|
|
||||||
{
|
|
||||||
get => _hideDeleteButton;
|
|
||||||
set => SetProperty(ref _hideDeleteButton, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool IsAd
|
|
||||||
{
|
|
||||||
get => _isAd;
|
|
||||||
set => SetProperty(ref _isAd, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public TwitchChannel? Channel => _channel ?? new TwitchChannel(ChannelName);
|
|
||||||
|
|
||||||
public SolidColorBrush ViewerCountColor
|
|
||||||
{
|
|
||||||
get => _viewerCountColor;
|
|
||||||
set => SetProperty(ref _viewerCountColor, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public MainWindowViewModel? MainVm { get; set; }
|
|
||||||
|
|
||||||
public DateTime? LastRaided => MainVm?.Database?.GetLastRaided(ChannelName);
|
|
||||||
|
|
||||||
public event EventHandler<ChannelDataChangedEventArgs>? ChannelDataChanged;
|
|
||||||
|
|
||||||
public RaidButtonViewModel(string channelName)
|
|
||||||
{
|
|
||||||
ChannelName = channelName;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<bool> GetOrUpdateChannelAsync()
|
|
||||||
{
|
|
||||||
Console.WriteLine("[DEBUG] Updating channel '{0}' ...", ChannelName);
|
|
||||||
|
|
||||||
var currentChannelData = await GetChannelAsync(ChannelName);
|
|
||||||
|
|
||||||
if (currentChannelData == null)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
var currentStreamData = await GetStreamAsync(currentChannelData);
|
|
||||||
|
|
||||||
var swapChannel = new TwitchChannel(ChannelName)
|
|
||||||
{
|
|
||||||
BroadcasterId = currentChannelData.Id,
|
|
||||||
Name = ChannelName,
|
|
||||||
DisplayName = currentChannelData.DisplayName,
|
|
||||||
IsLive = currentChannelData.IsLive,
|
|
||||||
ThumbnailUrl = currentChannelData.ThumbnailUrl,
|
|
||||||
ViewerCount = currentStreamData?.ViewerCount == null
|
|
||||||
? "(Offline)"
|
|
||||||
: $"{currentStreamData?.ViewerCount} Viewers",
|
|
||||||
Category = currentStreamData?.GameName
|
|
||||||
};
|
|
||||||
|
|
||||||
if (_channel != null)
|
|
||||||
{
|
|
||||||
_channel.PropertyChanged -= OnChannelDataChanged;
|
|
||||||
}
|
|
||||||
|
|
||||||
Dispatcher.UIThread.Invoke(() => {
|
|
||||||
ViewerCountColor = new SolidColorBrush(Color.FromRgb(
|
|
||||||
r: swapChannel.IsLive ? (byte) 0 : byte.MaxValue,
|
|
||||||
g: swapChannel.IsLive ? byte.MaxValue : (byte) 0,
|
|
||||||
b: 0)
|
|
||||||
);
|
|
||||||
|
|
||||||
_channel = swapChannel;
|
|
||||||
OnPropertyChanged(nameof(Channel));
|
|
||||||
});
|
|
||||||
|
|
||||||
if (_channel != null)
|
|
||||||
{
|
|
||||||
_channel.PropertyChanged += OnChannelDataChanged;
|
|
||||||
}
|
|
||||||
|
|
||||||
Console.WriteLine("[DEBUG] DONE Updating channel '{0}'", ChannelName);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task<Channel?> GetChannelAsync(string channelName)
|
|
||||||
{
|
|
||||||
if (App.TwitchApi == null)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(channelName))
|
|
||||||
return null;
|
|
||||||
|
|
||||||
var channels = await App.TwitchApi.Helix.Search.SearchChannelsAsync(channelName);
|
|
||||||
var exactChannel = channels.Channels.FirstOrDefault(c => c.BroadcasterLogin.Equals(channelName, StringComparison.CurrentCultureIgnoreCase));
|
|
||||||
|
|
||||||
return exactChannel;
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task<Stream?> GetStreamAsync(Channel currentChannelData)
|
|
||||||
{
|
|
||||||
if (App.TwitchApi == null)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
if (currentChannelData == null)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
var streams = await App.TwitchApi.Helix.Streams.GetStreamsAsync(userLogins: [currentChannelData.BroadcasterLogin]);
|
|
||||||
var exactStream = streams.Streams.FirstOrDefault(s => s.UserLogin == currentChannelData.BroadcasterLogin);
|
|
||||||
|
|
||||||
return exactStream;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task RaidChannel()
|
|
||||||
{
|
|
||||||
if (App.TwitchApi == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (Channel == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(App.TwitchBroadcasterId))
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (App.TwitchBroadcasterId == Channel.BroadcasterId)
|
|
||||||
return;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
await App.TwitchApi.Helix.Raids.StartRaidAsync(App.TwitchBroadcasterId, Channel.BroadcasterId);
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
Console.WriteLine(e.Message);
|
|
||||||
Console.WriteLine(e.StackTrace);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (MainVm?.Database != null)
|
|
||||||
{
|
|
||||||
MainVm.Database.SetRaided(ChannelName, DateTime.Now);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void RemoveChannel()
|
|
||||||
{
|
|
||||||
if (MainVm?.Database == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
MainVm.Database.RemoveChannel(ChannelName);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnChannelDataChanged(object? sender, PropertyChangedEventArgs e)
|
|
||||||
{
|
|
||||||
switch (e.PropertyName)
|
|
||||||
{
|
|
||||||
case "IsLive":
|
|
||||||
OnChannelDataChanged(ChannelDataChangedEventArgs.FromIsLive(false, true));
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "ViewerCount":
|
|
||||||
OnChannelDataChanged(ChannelDataChangedEventArgs.FromViewerCount(0, 10));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnChannelDataChanged(ChannelDataChangedEventArgs args)
|
|
||||||
{
|
|
||||||
ChannelDataChanged?.Invoke(this, args);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,6 +1,4 @@
|
|||||||
using Avalonia;
|
|
||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
using Avalonia.Markup.Xaml;
|
|
||||||
|
|
||||||
namespace BetterRaid.Views;
|
namespace BetterRaid.Views;
|
||||||
|
|
||||||
|
@ -1,6 +1,4 @@
|
|||||||
using Avalonia;
|
|
||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
using Avalonia.Markup.Xaml;
|
|
||||||
|
|
||||||
namespace BetterRaid.Views;
|
namespace BetterRaid.Views;
|
||||||
|
|
||||||
|
@ -1,12 +1,11 @@
|
|||||||
<Window xmlns="https://github.com/avaloniaui"
|
<Window xmlns="https://github.com/avaloniaui"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:vm="using:BetterRaid.ViewModels"
|
xmlns:vm="using:BetterRaid.ViewModels"
|
||||||
xmlns:br="using:BetterRaid"
|
|
||||||
xmlns:con="using:BetterRaid.Converters"
|
xmlns:con="using:BetterRaid.Converters"
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
xmlns:ai="using:AsyncImageLoader"
|
xmlns:ai="using:AsyncImageLoader"
|
||||||
xmlns:misc="clr-namespace:BetterRaid.Misc"
|
xmlns:misc="using:BetterRaid.Misc"
|
||||||
mc:Ignorable="d"
|
mc:Ignorable="d"
|
||||||
d:DesignWidth="600"
|
d:DesignWidth="600"
|
||||||
d:DesignHeight="800"
|
d:DesignHeight="800"
|
||||||
@ -122,7 +121,7 @@
|
|||||||
|
|
||||||
<ai:AdvancedImage Grid.Column="0"
|
<ai:AdvancedImage Grid.Column="0"
|
||||||
Grid.Row="0"
|
Grid.Row="0"
|
||||||
Source="{Binding ThumbnailUrl, TargetNullValue={x:Static br:App.ChannelPlaceholderImageUrl}}" />
|
Source="{Binding ThumbnailUrl, TargetNullValue={x:Static misc:Constants.ChannelPlaceholderImageUrl}}" />
|
||||||
|
|
||||||
<Border Grid.Column="0"
|
<Border Grid.Column="0"
|
||||||
Grid.Row="0"
|
Grid.Row="0"
|
||||||
|
@ -1,15 +1,4 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.ObjectModel;
|
|
||||||
using System.ComponentModel;
|
|
||||||
using System.IO;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
using Avalonia.Interactivity;
|
|
||||||
using Avalonia.Threading;
|
|
||||||
using BetterRaid.Extensions;
|
|
||||||
using BetterRaid.Models;
|
|
||||||
using BetterRaid.ViewModels;
|
|
||||||
|
|
||||||
namespace BetterRaid.Views;
|
namespace BetterRaid.Views;
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user