135 lines
4.6 KiB
C#
135 lines
4.6 KiB
C#
|
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();
|
||
|
}
|
||
|
}
|