parental-control/client/ParentalControlService/ParentalControlSvc.cs

135 lines
4.6 KiB
C#
Raw Normal View History

2023-09-09 10:41:07 +02:00
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System;
using System.Diagnostics;
using System.IO;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Net.Http.Json;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using System.Timers;
using Timer = System.Timers.Timer;
namespace ParentalControlService;
public class ParentalControlSvc : BackgroundService
{
private readonly string configPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "config.json");
private readonly string statusPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "status.json");
private readonly ILogger<ParentalControlSvc> _logger;
private Config config;
private Status status;
private bool currentAction;
private readonly Timer checkTimer;
private readonly HttpClient httpClient;
public ParentalControlSvc(ILogger<ParentalControlSvc> logger)
{
_logger = logger;
// Load status
try {
status = JsonSerializer.Deserialize<Status>(File.ReadAllText(statusPath))!;
}
catch (FileNotFoundException) {
status = new Status() { Action = true, Until = DateTime.MaxValue };
}
currentAction = status.Action;
// Load config
try
{
config = JsonSerializer.Deserialize<Config>(File.ReadAllText(configPath))!;
}
catch (FileNotFoundException)
{
config = new Config() { Username = "", Password = "" };
}
// Create HTTP Client
httpClient = new() { BaseAddress = new Uri(config.Url), Timeout = TimeSpan.FromSeconds(5) };
string credentials = Convert.ToBase64String(System.Text.Encoding.ASCII.GetBytes($"{config.Username}:{config.Password}"));
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", credentials);
httpClient.DefaultRequestHeaders.UserAgent.Add(new ProductInfoHeaderValue("ParentalControl", "1.1.0"));
// Initialize Timer
checkTimer = new(5000);
checkTimer.Elapsed += new ElapsedEventHandler(CheckTimerTick);
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
try {
await CheckTimerTick();
checkTimer.Start();
while (!stoppingToken.IsCancellationRequested) {
await Task.Delay(1000, stoppingToken);
}
}
catch (TaskCanceledException) {
checkTimer.Stop();
}
catch (Exception ex) {
_logger.LogError(ex, "{Message}", ex.Message);
Environment.Exit(1);
}
}
private void SaveStatus() {
File.WriteAllText(statusPath, JsonSerializer.Serialize(status));
}
private static void RunScript(string scriptName) {
scriptName = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, $"{scriptName}.ps1");
if (!File.Exists(scriptName)) {
return;
}
ProcessStartInfo psi = new() {
FileName = "powershell.exe",
Arguments = $"-ExecutionPolicy Bypass -File \"{scriptName}\"",
UseShellExecute = false,
WorkingDirectory = AppDomain.CurrentDomain.BaseDirectory,
};
Process.Start(psi);
}
private async Task CheckTimerTick() {
try {
Data data = await httpClient.GetFromJsonAsync<Data>("api/data");
if (data.Status != status) {
status = data.Status;
SaveStatus();
}
}
catch (Exception ex) when (ex is HttpRequestException or TaskCanceledException) {
_logger.LogError(ex, "{Message}", ex.Message);
}
if (status.Until != null && DateTime.Now >= status.Until) {
// Fetch failed, we need to advance the schedule on our own
status.Action = !status.Action;
status.Until = status.ThenUntil;
status.ThenUntil = null;
SaveStatus();
}
if (currentAction == status.Action) {
return;
}
currentAction = !currentAction;
if (currentAction) {
_logger.LogInformation("[{time}] Unblocked", DateTime.Now);
RunScript("unblock");
}
else {
_logger.LogInformation("[{time}] Blocked", DateTime.Now);
RunScript("block");
}
}
private async void CheckTimerTick(object? sender, ElapsedEventArgs e) {
await CheckTimerTick();
}
}