init
This commit is contained in:
245
src/Service/Jobs/ChineseNfoRegistry.cs
Normal file
245
src/Service/Jobs/ChineseNfoRegistry.cs
Normal file
@@ -0,0 +1,245 @@
|
||||
using System.Net;
|
||||
using System.Text.Json.Nodes;
|
||||
using System.Xml;
|
||||
using FluentScheduler;
|
||||
using Interface.Jobs;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Service.Jobs;
|
||||
|
||||
public class ChineseNfoRegistry : Registry, IChineseNfoRegistry
|
||||
{
|
||||
private readonly IConfiguration _configuration;
|
||||
private readonly ILogger<ChineseNfoRegistry> _logger;
|
||||
private readonly HttpClient _client;
|
||||
|
||||
public ChineseNfoRegistry(IConfiguration configuration, ILogger<ChineseNfoRegistry> logger)
|
||||
{
|
||||
_configuration = configuration;
|
||||
_logger = logger;
|
||||
|
||||
var proxyAddress = _configuration["ChineseNfo:HttpProxy"];
|
||||
var httpClientHandler = new HttpClientHandler
|
||||
{
|
||||
Proxy = string.IsNullOrEmpty(proxyAddress) ? null : new WebProxy(proxyAddress, false),
|
||||
UseProxy = !string.IsNullOrEmpty(proxyAddress)
|
||||
};
|
||||
|
||||
_client = new HttpClient(httpClientHandler);
|
||||
_client.BaseAddress = new Uri("https://api.themoviedb.org");
|
||||
|
||||
Schedule(() => Job(true, true)).ToRunEvery(1).Days();
|
||||
}
|
||||
|
||||
public async void Job(bool ignoreLocked, bool ignoreCompleted)
|
||||
{
|
||||
try
|
||||
{
|
||||
await JobExecute(ignoreLocked, ignoreCompleted);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.LogError(e, "ChineseNfoRegistry.Job() error");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private async Task JobExecute(bool ignoreLocked, bool ignoreCompleted)
|
||||
{
|
||||
// 读取环境变量 tv folder
|
||||
var tvFolder = _configuration["ChineseNfo:TvFolder"];
|
||||
if (string.IsNullOrEmpty(tvFolder))
|
||||
{
|
||||
Console.WriteLine("请设置环境变量 tv_folder");
|
||||
return;
|
||||
}
|
||||
|
||||
// 扫描 tvshow.nfo 文件
|
||||
var tvShowFiles = Directory.GetFiles(tvFolder, "tvshow.nfo", SearchOption.AllDirectories);
|
||||
|
||||
foreach (var tvShowFile in tvShowFiles)
|
||||
{
|
||||
var nfoContent = File.ReadAllText(tvShowFile);
|
||||
|
||||
// 读取 使用XML解析器 读取 <uniqueid type="tmdb">60059</uniqueid>
|
||||
var tvXml = new XmlDocument();
|
||||
tvXml.LoadXml(nfoContent);
|
||||
var uniqueIdNode = tvXml.SelectSingleNode("//uniqueid[@type='tmdb']");
|
||||
if (uniqueIdNode == null)
|
||||
{
|
||||
Console.WriteLine($"{tvShowFile} & 未找到 tmdb id");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!int.TryParse(uniqueIdNode.InnerText, out var tmdbId))
|
||||
{
|
||||
Console.WriteLine($"{tvShowFile} & tmdb id 不是数字");
|
||||
continue;
|
||||
}
|
||||
|
||||
// 获取 tvShowFile 的文件夹
|
||||
var folderPath = Path.GetDirectoryName(tvShowFile);
|
||||
|
||||
if (folderPath == null)
|
||||
{
|
||||
Console.WriteLine($"{tvShowFile} & 未找到文件夹");
|
||||
continue;
|
||||
}
|
||||
|
||||
// 扫描文件夹下其他 nfo 文件
|
||||
var seasonFiles = Directory.GetFiles(folderPath, "*.nfo", SearchOption.AllDirectories);
|
||||
|
||||
seasonFiles = seasonFiles.Where(x => !x.EndsWith("tvshow.nfo")).ToArray();
|
||||
|
||||
foreach (var seasonFile in seasonFiles)
|
||||
{
|
||||
var episodeContent = File.ReadAllText(seasonFile);
|
||||
var episodeXml = new XmlDocument();
|
||||
episodeXml.LoadXml(episodeContent);
|
||||
|
||||
// 判断有无 completed 节点
|
||||
var completedNode = episodeXml.SelectSingleNode("//completed");
|
||||
if (completedNode != null && ignoreCompleted == false)
|
||||
{
|
||||
Console.WriteLine($"{seasonFile} & 已完成");
|
||||
continue;
|
||||
}
|
||||
|
||||
// 判断 locked == true
|
||||
var lockedNode = episodeXml.SelectSingleNode("//lockdata");
|
||||
if (lockedNode != null && lockedNode.InnerText == "true" && ignoreLocked == false)
|
||||
{
|
||||
Console.WriteLine($"{seasonFile} & 已锁定");
|
||||
continue;
|
||||
}
|
||||
|
||||
// 读取 <season>1</season>
|
||||
var seasonNode = episodeXml.SelectSingleNode("//season");
|
||||
if (seasonNode == null)
|
||||
{
|
||||
Console.WriteLine($"{seasonFile} & 未找到 season");
|
||||
continue;
|
||||
}
|
||||
|
||||
// 读取 <episode>1</episode>
|
||||
var episodeNode = episodeXml.SelectSingleNode("//episode");
|
||||
if (episodeNode == null)
|
||||
{
|
||||
Console.WriteLine($"{seasonFile} & 未找到 episode");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!int.TryParse(seasonNode.InnerText, out var season))
|
||||
{
|
||||
Console.WriteLine($"{seasonFile} & season 不是数字");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!int.TryParse(episodeNode.InnerText, out var episode))
|
||||
{
|
||||
Console.WriteLine($"{seasonFile} & episode 不是数字");
|
||||
continue;
|
||||
}
|
||||
|
||||
// 设置 title
|
||||
var titleNode = episodeXml.SelectSingleNode("//title");
|
||||
if (titleNode == null)
|
||||
{
|
||||
Console.WriteLine($"{seasonFile} & 未找到 title");
|
||||
continue;
|
||||
}
|
||||
|
||||
var json = await GetTmdbEpisode(tmdbId, season, episode);
|
||||
|
||||
if (json == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var title = json["name"]?.ToString();
|
||||
var overview = json["overview"]?.ToString();
|
||||
|
||||
var isUpdate = false;
|
||||
if (!string.IsNullOrEmpty(title))
|
||||
{
|
||||
title = $"<![CDATA[{title}]]>";
|
||||
|
||||
if (titleNode.InnerXml != title)
|
||||
{
|
||||
isUpdate = true;
|
||||
// 写入且不转义
|
||||
titleNode.InnerXml = title;
|
||||
}
|
||||
}
|
||||
|
||||
var plotNode = episodeXml.SelectSingleNode("//plot");
|
||||
|
||||
if (!string.IsNullOrEmpty(overview) && plotNode != null)
|
||||
{
|
||||
overview = $"<![CDATA[{overview}]]>";
|
||||
if (plotNode.InnerXml != overview)
|
||||
{
|
||||
isUpdate = true;
|
||||
plotNode.InnerXml = overview;
|
||||
}
|
||||
}
|
||||
|
||||
if (isUpdate)
|
||||
{
|
||||
// 添加一个已完成节点
|
||||
if (completedNode == null)
|
||||
{
|
||||
episodeXml.DocumentElement?.AppendChild(episodeXml.CreateElement("completed"));
|
||||
}
|
||||
|
||||
// 锁定
|
||||
if (lockedNode == null)
|
||||
{
|
||||
lockedNode = episodeXml.CreateElement("lockdata");
|
||||
episodeXml.DocumentElement?.AppendChild(lockedNode);
|
||||
lockedNode.InnerText = "true";
|
||||
}
|
||||
else
|
||||
{
|
||||
lockedNode.InnerText = "true";
|
||||
}
|
||||
|
||||
episodeXml.Save(seasonFile);
|
||||
Console.WriteLine($"{seasonFile} & {title} & {overview}");
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine($"{seasonFile} & 无更新");
|
||||
}
|
||||
|
||||
await Task.Delay(10000);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<JsonObject?> GetTmdbEpisode(int tmdbId, int season, int episode)
|
||||
{
|
||||
try
|
||||
{
|
||||
const string episodeUrl = "/3/tv/{0}/season/{1}/episode/{2}?api_key=e28e1bc408db7adefc8bacce225c5085&language=zh-CN";
|
||||
|
||||
var requestUrl = string.Format(episodeUrl, tmdbId, season, episode);
|
||||
var response = await _client.GetAsync(requestUrl);
|
||||
if (!response.IsSuccessStatusCode)
|
||||
{
|
||||
Console.WriteLine($"{requestUrl} & {response.StatusCode}");
|
||||
return null;
|
||||
}
|
||||
|
||||
var content = await response.Content.ReadAsStringAsync();
|
||||
var json = JsonNode.Parse(content);
|
||||
return json as JsonObject;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Console.WriteLine($"{tmdbId} & {season} & {episode} & {e.Message}");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
174
src/Service/Jobs/DiskActionMonitorRegistry.cs
Normal file
174
src/Service/Jobs/DiskActionMonitorRegistry.cs
Normal file
@@ -0,0 +1,174 @@
|
||||
using Core;
|
||||
using FluentScheduler;
|
||||
using Interface.Jobs;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Service.Jobs;
|
||||
|
||||
public enum DiskNotifyType
|
||||
{
|
||||
Action,
|
||||
Sleep
|
||||
}
|
||||
|
||||
public class DiskActionMonitorRegistry : Registry, IDiskActionMonitorRegistry
|
||||
{
|
||||
private readonly IConfiguration _configuration;
|
||||
private static Dictionary<string, int> _lastReadCount = new();
|
||||
private static Dictionary<string, int> _lastWriteCount = new();
|
||||
private readonly ILogger<DiskActionMonitorRegistry> _logger;
|
||||
private static readonly Dictionary<string, List<(DateTime, DiskNotifyType)>> LatestNotifyTimes = new();
|
||||
private static readonly Dictionary<string, DateTime> LastChangedTimes = new();
|
||||
|
||||
public DiskActionMonitorRegistry(IConfiguration configuration, ILogger<DiskActionMonitorRegistry> logger)
|
||||
{
|
||||
_configuration = configuration;
|
||||
_logger = logger;
|
||||
Schedule(Job).ToRunEvery(5).Seconds();
|
||||
}
|
||||
|
||||
public async void Job()
|
||||
{
|
||||
try
|
||||
{
|
||||
await JobExecute();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.LogError(e, "DiskActionMonitorRegistry.Job() error");
|
||||
}
|
||||
}
|
||||
|
||||
private async Task JobExecute()
|
||||
{
|
||||
var filePath = _configuration["DiskActionMonitor:FilePath"] ?? throw new ArgumentNullException($"DiskActionMonitor:FilePath");
|
||||
var disks = _configuration
|
||||
.GetSection("DiskActionMonitor:Disks")
|
||||
.GetChildren()
|
||||
.Select(x => x.Value);
|
||||
|
||||
|
||||
foreach (var disk in disks)
|
||||
{
|
||||
if (string.IsNullOrEmpty(disk))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!_lastReadCount.ContainsKey(disk))
|
||||
{
|
||||
_lastReadCount.Add(disk, 0);
|
||||
}
|
||||
|
||||
if (!_lastWriteCount.ContainsKey(disk))
|
||||
{
|
||||
_lastWriteCount.Add(disk, 0);
|
||||
}
|
||||
|
||||
if (!LatestNotifyTimes.ContainsKey(disk))
|
||||
{
|
||||
LatestNotifyTimes.Add(disk, new());
|
||||
}
|
||||
|
||||
if (!LastChangedTimes.ContainsKey(disk))
|
||||
{
|
||||
LastChangedTimes.Add(disk, DateTime.Now);
|
||||
}
|
||||
|
||||
var content = await File.ReadAllTextAsync(string.Format(filePath, disk));
|
||||
|
||||
var cols = content.Split(" ", StringSplitOptions.RemoveEmptyEntries);
|
||||
|
||||
var readCount = TryGetByIndex(cols, 2);
|
||||
var writeCount = TryGetByIndex(cols, 6);
|
||||
|
||||
if (!int.TryParse(readCount, out var read) || !int.TryParse(writeCount, out var write))
|
||||
{
|
||||
continue; // 读取失败
|
||||
}
|
||||
|
||||
var readDiff = read - _lastReadCount[disk];
|
||||
var writeDiff = write - _lastWriteCount[disk];
|
||||
|
||||
_lastReadCount[disk] = read;
|
||||
_lastWriteCount[disk] = write;
|
||||
|
||||
if (readDiff > 0 || writeDiff > 0)
|
||||
{
|
||||
_logger.LogInformation($"[{disk}] ReadDiff: {readDiff}, WriteDiff: {writeDiff}");
|
||||
}
|
||||
|
||||
if (readDiff > 10 || writeDiff > 10)
|
||||
{
|
||||
// 上一次通知的是休眠或者没有通知 发现有变换通知变化
|
||||
if (LatestNotifyTimes[disk].Any() == false
|
||||
|| LatestNotifyTimes[disk].Last().Item2 == DiskNotifyType.Sleep)
|
||||
{
|
||||
LatestNotifyTimes[disk].Add((DateTime.Now, DiskNotifyType.Action));
|
||||
|
||||
await WxNotify.SendDiskInfoAsync(@$"
|
||||
## 磁盘 <font color='info'> {disk} </font> 刚发生读写操作 🙋
|
||||
|
||||
> 当前累计读: <font color='warning'> {GetEasyReadNumber(read)} </font>、新增读: <font color='warning'> {GetEasyReadNumber(readDiff)} </font>
|
||||
> 当前累计写: <font color='warning'> {GetEasyReadNumber(write)} </font>、新增写: <font color='warning'> {GetEasyReadNumber(writeDiff)} </font>
|
||||
|
||||
当前时间: <font color='comment'> {DateTime.Now:yyyy-M-d H:m:s} </font>");
|
||||
}
|
||||
else if (DateTime.Now - LatestNotifyTimes[disk].Last().Item1 > TimeSpan.FromHours(1))
|
||||
{
|
||||
LatestNotifyTimes[disk].Add((DateTime.Now, DiskNotifyType.Action));
|
||||
|
||||
await WxNotify.SendDiskInfoAsync(@$"
|
||||
## 磁盘 <font color='info'> {disk} </font> 连续运行超一小时 👨💻
|
||||
|
||||
> 当前累计读: <font color='warning'> {GetEasyReadNumber(read)} </font>
|
||||
> 当前累计写: <font color='warning'> {GetEasyReadNumber(write)} </font>
|
||||
|
||||
当前时间: <font color='comment'> {DateTime.Now:yyyy-M-d H:m:s} </font>");
|
||||
}
|
||||
|
||||
_lastReadCount[disk] = read;
|
||||
_lastWriteCount[disk] = write;
|
||||
LastChangedTimes[disk] = DateTime.Now;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// 没有变化 上一次是启动通知且超过10分钟,通知休眠
|
||||
if (LatestNotifyTimes[disk].Any() == false
|
||||
|| (LatestNotifyTimes[disk].Last().Item2 == DiskNotifyType.Action
|
||||
&& DateTime.Now - LastChangedTimes[disk] > TimeSpan.FromMinutes(10)))
|
||||
{
|
||||
LatestNotifyTimes[disk].Add((DateTime.Now, DiskNotifyType.Sleep));
|
||||
|
||||
await WxNotify.SendDiskInfoAsync(@$"
|
||||
## 磁盘 <font color='info'> {disk} </font> 长时间无读写变化 😪
|
||||
|
||||
> 上一次读写时间:{LatestNotifyTimes[disk].Last(x => x.Item2 == DiskNotifyType.Action).Item1:yyyy-M-d H:m:s}
|
||||
|
||||
> 当前累计读: <font color='warning'> {GetEasyReadNumber(read)} </font>
|
||||
> 当前累计写: <font color='warning'> {GetEasyReadNumber(write)} </font>
|
||||
|
||||
当前时间: <font color='comment'> {DateTime.Now:yyyy-M-d H:m:s} </font>");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private string TryGetByIndex(string[] arr, int index) => index < arr.Length ? arr[index] : string.Empty;
|
||||
|
||||
private string GetEasyReadNumber(int number)
|
||||
{
|
||||
if (number < 10000)
|
||||
{
|
||||
return number.ToString();
|
||||
}
|
||||
|
||||
if (number < 100000000)
|
||||
{
|
||||
return $"{number / 10000M:0.##} 万";
|
||||
}
|
||||
|
||||
return $"{number / 100000000M:0.##} 亿";
|
||||
}
|
||||
}
|
||||
95
src/Service/Jobs/HealthyTaskRegistry.cs
Normal file
95
src/Service/Jobs/HealthyTaskRegistry.cs
Normal file
@@ -0,0 +1,95 @@
|
||||
using System.Diagnostics;
|
||||
using System.Net;
|
||||
using System.Net.Http.Headers;
|
||||
using System.Net.Http.Json;
|
||||
using System.Text;
|
||||
using Core;
|
||||
using FluentScheduler;
|
||||
using Interface.Jobs;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
|
||||
namespace Service.Jobs;
|
||||
|
||||
public class HealthyTaskRegistry : Registry, IHealthyTaskRegistry
|
||||
{
|
||||
private readonly IConfiguration _configuration;
|
||||
|
||||
public HealthyTaskRegistry(IConfiguration configuration)
|
||||
{
|
||||
_configuration = configuration;
|
||||
Schedule(Job).ToRunNow().AndEvery(2).Minutes();
|
||||
}
|
||||
|
||||
public async void Job()
|
||||
{
|
||||
try
|
||||
{
|
||||
await JobExecute();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Console.WriteLine(e);
|
||||
await WxNotify.SendCommonAsync($"HealthyTaskRegistry.Job() error: {e}");
|
||||
}
|
||||
}
|
||||
|
||||
private async Task JobExecute()
|
||||
{
|
||||
// 获取 配置 是个 KV 集合
|
||||
var configs = _configuration.GetSection("HealthyTasks");
|
||||
|
||||
// 遍历配置
|
||||
foreach (var item in configs.GetChildren())
|
||||
{
|
||||
// 获取配置的 containerName 和 url
|
||||
var containerName = item["ContainerName"];
|
||||
var url = item["Url"];
|
||||
|
||||
// 执行
|
||||
await ExecuteItem(containerName!, url!);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task ExecuteItem(string containerName, string url)
|
||||
{
|
||||
try
|
||||
{
|
||||
var client = new HttpClient();
|
||||
|
||||
// basic auth
|
||||
string authInfo = Convert.ToBase64String(Encoding.ASCII.GetBytes("suncheng:SCsunch940622"));
|
||||
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", authInfo);
|
||||
|
||||
var response = await client.GetAsync(url);
|
||||
|
||||
if (response.StatusCode != HttpStatusCode.OK)
|
||||
{
|
||||
throw new Exception($"StatusCode: {response.StatusCode}");
|
||||
}
|
||||
|
||||
Console.WriteLine($"ContainerName: {containerName}, Url: {url}, StatusCode: {response.StatusCode}");
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Console.WriteLine(e);
|
||||
await WxNotify.SendCommonAsync($"ExecuteItem {containerName} error: {e.Message}");
|
||||
await ExecuteReboot(containerName);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task ExecuteReboot(string containerName)
|
||||
{
|
||||
var command = $"docker restart {containerName}";
|
||||
|
||||
var (responseCode, message) = await Command.ExecAsync(command);
|
||||
|
||||
if (responseCode != HttpStatusCode.OK)
|
||||
{
|
||||
await WxNotify.SendCommonAsync($"ExecuteReboot {containerName} error: {responseCode} message: {message}");
|
||||
}
|
||||
else
|
||||
{
|
||||
await WxNotify.SendCommonAsync($"ContainerName: {containerName}");
|
||||
}
|
||||
}
|
||||
}
|
||||
106
src/Service/Jobs/LogTotalNotifyJobRegistry.cs
Normal file
106
src/Service/Jobs/LogTotalNotifyJobRegistry.cs
Normal file
@@ -0,0 +1,106 @@
|
||||
using System.Text;
|
||||
using Core;
|
||||
using FluentScheduler;
|
||||
using Interface.Jobs;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace Service.Jobs;
|
||||
|
||||
public class LogTotalNotifyJobRegistry : Registry, ILogTotalNotifyJobRegistry
|
||||
{
|
||||
private readonly IConfiguration _configuration;
|
||||
|
||||
public LogTotalNotifyJobRegistry(IConfiguration configuration)
|
||||
{
|
||||
_configuration = configuration;
|
||||
Schedule(Job).ToRunEvery(1).Days().At(8, 30);
|
||||
}
|
||||
|
||||
public async void Job()
|
||||
{
|
||||
try
|
||||
{
|
||||
// await JobExecute();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Console.WriteLine(e);
|
||||
await WxNotify.SendCommonAsync($"LogTotalNotifyJobRegistry.Job() error: {e}");
|
||||
}
|
||||
}
|
||||
|
||||
private async Task JobExecute()
|
||||
{
|
||||
var client = new HttpClient();
|
||||
|
||||
client.DefaultRequestHeaders.Add("Authorization", $"Bearer {_configuration["Grafana:Token"]}");
|
||||
var requestBody =
|
||||
"""
|
||||
{
|
||||
"queries": [
|
||||
{
|
||||
"datasource": {
|
||||
"type": "loki",
|
||||
"uid": "edf5cwf6n6i2oe"
|
||||
},
|
||||
"editorMode": "builder",
|
||||
"expr": "sum by(container_name) (count_over_time({compose_project=~\"dockers|immich|nasrobot|elasticsearch|webui-docker\"} [1h]))",
|
||||
"queryType": "range",
|
||||
"refId": "A",
|
||||
"datasourceId": 2,
|
||||
"intervalMs": 3600000
|
||||
}
|
||||
],
|
||||
"from": "1708963200000",
|
||||
"to": "1709049600000"
|
||||
}
|
||||
""";
|
||||
|
||||
requestBody = requestBody.Replace("1708963200000", DateTime.Today.AddDays(-2).ToUnixTimeMilliseconds().ToString());
|
||||
requestBody = requestBody.Replace("1709049600000", DateTime.Today.AddDays(-1).ToUnixTimeMilliseconds().ToString());
|
||||
|
||||
var content = new StringContent(requestBody, Encoding.UTF8, "application/json");
|
||||
var response = await client.PostAsync(_configuration["Grafana:LokiUrl"], content);
|
||||
|
||||
var result = await response.Content.ReadAsStringAsync();
|
||||
|
||||
var jObject = JObject.Parse(result);
|
||||
var frames = jObject["results"]?["A"]?["frames"];
|
||||
|
||||
if (frames == null)
|
||||
{
|
||||
throw new Exception("frames is null");
|
||||
}
|
||||
|
||||
var msg = new StringBuilder();
|
||||
msg.AppendLine($"## {DateTime.Today.AddDays(-1):yyyy-MM-dd}日志明细如下:");
|
||||
var total = 0;
|
||||
var kv = new Dictionary<string, int>();
|
||||
foreach (var item in frames)
|
||||
{
|
||||
var name = item["schema"]?["fields"]?.LastOrDefault()?["labels"]?["container_name"]?.ToString();
|
||||
|
||||
if (string.IsNullOrEmpty(name))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var values = item["data"]?["values"]?.LastOrDefault()?.ToObject<int[]>();
|
||||
|
||||
var value = values?.Sum() ?? 0;
|
||||
total += value;
|
||||
kv[name] = value;
|
||||
}
|
||||
|
||||
foreach (var (key, value) in kv.OrderByDescending(x => x.Value))
|
||||
{
|
||||
msg.AppendLine($"<font color='info'>**{key}**</font>: <font color='warning'>{value}</font>");
|
||||
}
|
||||
|
||||
msg.AppendLine("");
|
||||
msg.AppendLine($"> **总计**: <font color='warning'>{total}</font>");
|
||||
|
||||
await WxNotify.SendCommonAsync(msg.ToString());
|
||||
}
|
||||
}
|
||||
69
src/Service/Jobs/RSyncTaskRegistry.cs
Normal file
69
src/Service/Jobs/RSyncTaskRegistry.cs
Normal file
@@ -0,0 +1,69 @@
|
||||
using System.Diagnostics;
|
||||
using System.Net.Http.Json;
|
||||
using Core;
|
||||
using FluentScheduler;
|
||||
using Interface.Jobs;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
|
||||
namespace Service.Jobs;
|
||||
|
||||
public class RSyncTaskRegistry : Registry, IRSyncTaskRegistry
|
||||
{
|
||||
private readonly IConfiguration _configuration;
|
||||
|
||||
public RSyncTaskRegistry(IConfiguration configuration)
|
||||
{
|
||||
_configuration = configuration;
|
||||
Schedule(Job).ToRunEvery(1).Days().At(10, 0);
|
||||
}
|
||||
|
||||
public async void Job()
|
||||
{
|
||||
try
|
||||
{
|
||||
// await JobExecute();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Console.WriteLine(e);
|
||||
await WxNotify.SendCommonAsync($"RSyncTaskRegistry.Job() error: {e}");
|
||||
}
|
||||
}
|
||||
|
||||
private async Task JobExecute()
|
||||
{
|
||||
var config = _configuration.GetSection("SyncTask");
|
||||
|
||||
foreach (var item in config.GetSection("SyncPaths").GetChildren())
|
||||
{
|
||||
var source = Path.Combine(config["SourceRoot"]!, item["Source"]!);
|
||||
var isDeleteAfter = item["DeleteAfter"] == "true";
|
||||
await ExecuteItem(source, config["TargetRoot"]!, item["Target"]!, isDeleteAfter);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task ExecuteItem(string source, string remote, string destination, bool isDeleteAfter)
|
||||
{
|
||||
var logName = $"rclone_output_{destination.Replace("/", "_")}{DateTime.Now:yyMMddHHmm}.log";
|
||||
|
||||
var commands = new[]
|
||||
{
|
||||
$"rclone sync " +
|
||||
$"{source} " +
|
||||
$"{remote}:{destination} " +
|
||||
$"--fast-list " +
|
||||
$"--size-only " +
|
||||
$"{(isDeleteAfter ? "--delete-after" : "--delete-excluded")} " +
|
||||
$"> /wd/logs/{logName} 2>&1"
|
||||
};
|
||||
|
||||
await WxNotify.SendCommonAsync($@"RSyncTaskRegistry.ExecuteItem()
|
||||
|
||||
`{commands[0]}`
|
||||
|
||||
> {DateTime.Now:yyyy-MM-dd HH:mm:ss}
|
||||
");
|
||||
|
||||
_ = Command.ExecAsync(commands);
|
||||
}
|
||||
}
|
||||
61
src/Service/Jobs/ShutdownRegistry.cs
Normal file
61
src/Service/Jobs/ShutdownRegistry.cs
Normal file
@@ -0,0 +1,61 @@
|
||||
using System.Net;
|
||||
using Core;
|
||||
using FluentScheduler;
|
||||
using Interface.Jobs;
|
||||
|
||||
namespace Service.Jobs;
|
||||
|
||||
public class ShutdownRegistry : Registry, IShutdownRegistry
|
||||
{
|
||||
public ShutdownRegistry()
|
||||
{
|
||||
Schedule(Job).ToRunEvery(1).Days().At(23, 30);
|
||||
}
|
||||
|
||||
public async void Job()
|
||||
{
|
||||
try
|
||||
{
|
||||
await JobExecute();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Console.WriteLine(e);
|
||||
await WxNotify.SendCommonAsync($"ShutdownRegistry.Job() error: {e}");
|
||||
}
|
||||
}
|
||||
|
||||
private async Task JobExecute()
|
||||
{
|
||||
var command = "shutdown 9";
|
||||
|
||||
var (responseCode, msg) = await Command.ExecAsync(command);
|
||||
|
||||
var wxMsg = $@"
|
||||
# 定时关机指令执行
|
||||
|
||||
> 执行结果:{responseCode}
|
||||
> 执行消息:{msg}
|
||||
|
||||
如需取消关机请点击:[取消关机](http://suncheng.online:35642/api/JobTrigger/CancelShutdown)
|
||||
|
||||
";
|
||||
|
||||
await WxNotify.SendCommonAsync(wxMsg);
|
||||
|
||||
if (responseCode == HttpStatusCode.OK)
|
||||
{
|
||||
// 每三分钟提醒一次
|
||||
_ = Task.Delay(3 * 60 * 1000).ContinueWith(async _ => await WxNotify.SendCommonAsync(wxMsg));
|
||||
}
|
||||
}
|
||||
|
||||
public async Task CancelShutdown()
|
||||
{
|
||||
var command = "shutdown -c";
|
||||
|
||||
var (responseCode, _) = await Command.ExecAsync(command);
|
||||
|
||||
await WxNotify.SendCommonAsync($"取消关机指令执行完成 {responseCode}");
|
||||
}
|
||||
}
|
||||
46
src/Service/Jobs/StartupRegistry.cs
Normal file
46
src/Service/Jobs/StartupRegistry.cs
Normal file
@@ -0,0 +1,46 @@
|
||||
using System.Net;
|
||||
using Core;
|
||||
using FluentScheduler;
|
||||
using Interface.Jobs;
|
||||
|
||||
namespace Service.Jobs;
|
||||
|
||||
public class StartupRegistry : Registry, IStartupRegistry
|
||||
{
|
||||
public StartupRegistry()
|
||||
{
|
||||
Schedule(Job).ToRunNow();
|
||||
}
|
||||
|
||||
public async void Job()
|
||||
{
|
||||
try
|
||||
{
|
||||
// await JobExecute();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Console.WriteLine(e);
|
||||
await WxNotify.SendCommonAsync($"StartupRegistry.Job() error: {e}");
|
||||
}
|
||||
}
|
||||
|
||||
private async Task JobExecute()
|
||||
{
|
||||
var commands = new[]
|
||||
{
|
||||
"chmod 777 /var/run/docker.sock",
|
||||
};
|
||||
|
||||
var (responseCode, _) = await Command.ExecAsync(commands);
|
||||
|
||||
if (responseCode != HttpStatusCode.OK)
|
||||
{
|
||||
await WxNotify.SendCommonAsync($"StartupRegistry.Job() error: {responseCode}");
|
||||
}
|
||||
else
|
||||
{
|
||||
await WxNotify.SendCommonAsync("StartupRegistry.Job() success");
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user