Files

159 lines
6.0 KiB
C#

using System.Collections.Specialized;
using System.Globalization;
using CompatBot.Utils.ResultFormatters;
using Microsoft.Extensions.Caching.Memory;
namespace CompatBot.Database.Providers;
internal static class HwInfoProvider
{
private static readonly Encoding Utf8 = new UTF8Encoding(false);
private static readonly TimeSpan CacheTime = TimeSpan.FromDays(1);
private static readonly MemoryCache UserCache = new(new MemoryCacheOptions { ExpirationScanFrequency = TimeSpan.FromHours(1) });
public static async ValueTask AddOrUpdateSystemAsync(DiscordClient client, DiscordMessage msg, NameValueCollection items, CancellationToken cancellationToken)
{
var ignoreAuthor = await msg.Author.IsWhitelistedAsync(client, msg.Channel.Guild).ConfigureAwait(false);
byte counter = 0;
if (!ignoreAuthor
&& UserCache.TryGetValue(msg.Author.Id, out counter)
&& counter > 4)
{
Config.Log.Debug($"Ignoring HW report for user {msg.Author.Id} ({msg.Author.Username}#{msg.Author.Discriminator})");
return;
}
if (items["cpu_model"] is not string cpuString
|| (items["gpu_name"] ?? items["gpu_info"]) is not string gpuString
|| !int.TryParse(items["thread_count"], out var threadCount)
|| !decimal.TryParse(items["memory_amount"], out var ramGB)
|| items["cpu_extensions"] is not string cpuExtensions)
return;
var cpuStringParts = cpuString.Split(' ', 2);
var gpuStringParts = gpuString.Split(' ', 2);
if (cpuStringParts.Length != 2 || gpuStringParts.Length != 2)
return;
if (cpuStringParts[0].ToLower() is not ("intel" or "amd" or "apple"))
{
Config.Log.Warn($"Unknown CPU maker {cpuStringParts[0]}, plz fix");
return;
}
if (gpuStringParts[0].ToLower() is not ("nvidia" or "amd" or "ati" or "intel" or "apple"))
if (LogParserResult.IsNvidia(gpuString))
gpuStringParts = ["NVIDIA", gpuString];
else if (LogParserResult.IsAmd(gpuString))
gpuStringParts = ["AMD", gpuString];
else
{
Config.Log.Warn($"Unknown GPU maker {gpuStringParts[0]}, plz fix");
return;
}
var ts = msg.Timestamp.UtcDateTime;
if (items["log_start_timestamp"] is string logTs
&& DateTime.TryParse(logTs, DateTimeFormatInfo.InvariantInfo, DateTimeStyles.AssumeUniversal, out var logTsVal))
ts = logTsVal.ToUniversalTime();
var osType = GetOsType(items["os_type"]);
var info = new HwInfo
{
Timestamp = ts.Ticks,
InstallId = GetHwId(items, msg),
CpuMaker = cpuStringParts[0],
CpuModel = cpuStringParts[1],
ThreadCount = threadCount,
CpuFeatures = GetFeatures(cpuExtensions),
RamInMb = (int)(ramGB * 1024),
GpuMaker = gpuStringParts[0],
GpuModel = gpuStringParts[1],
OsType = osType,
OsName = GetName(osType, items),
OsVersion = items["os_version"],
};
await using var wdb = await HardwareDb.OpenWriteAsync().ConfigureAwait(false);
var existingItem = await wdb.HwInfo.FindAsync([info.InstallId], cancellationToken: cancellationToken).ConfigureAwait(false);
if (existingItem is null)
wdb.HwInfo.Add(info);
else if (existingItem.Timestamp <= info.Timestamp)
wdb.Entry(existingItem).CurrentValues.SetValues(info);
try
{
await wdb.SaveChangesAsync(cancellationToken).ConfigureAwait(false);
}
catch (Exception e)
{
Config.Log.Error(e, "Failed to update hardware db");
}
if (!ignoreAuthor)
UserCache.Set(msg.Author.Id, counter + 1, CacheTime);
}
private static byte[] GetHwId(NameValueCollection items, DiscordMessage message)
{
var id = items["hw_id"] ?? (message.Author.Id.ToString("x16") + items["compat_database_path"]);
return Utf8.GetBytes(id).GetSaltedHash();
}
private static CpuFeatures GetFeatures(string extensions)
{
var result = CpuFeatures.None;
foreach (var ext in extensions.Split('|', StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries))
{
if (ext.StartsWith("AVX"))
{
result |= CpuFeatures.Avx;
if (ext.EndsWith('x'))
result |= CpuFeatures.Xop;
if (ext.Contains("512"))
{
result |= CpuFeatures.Avx512;
if (ext.Contains('+'))
result |= CpuFeatures.Avx512IL;
}
else if (ext.Contains('+'))
result |= CpuFeatures.Avx2;
}
else if (ext.StartsWith("FMA"))
{
if (ext.Contains('3'))
result |= CpuFeatures.Fma3;
if (ext.Contains('4'))
result |= CpuFeatures.Fma4;
}
else if (ext.StartsWith("TSX"))
{
result |= CpuFeatures.Tsx;
if (ext.Contains("TSX-FA"))
result |= CpuFeatures.TsxFa;
}
}
return result;
}
private static OsType GetOsType(string? osType)
=> osType switch
{
"Windows" => OsType.Windows,
"Linux" => OsType.Linux,
"MacOS" => OsType.MacOs,
"" => OsType.Unknown,
null => OsType.Unknown,
_ => OsType.Bsd,
};
private static string? GetName(OsType osType, NameValueCollection items)
=> osType switch
{
OsType.Windows => items["os_windows_version"],
OsType.Linux => items["os_linux_version"],
OsType.MacOs => items["os_mac_version"],
OsType.Bsd => items["os_linux_version"],
_ => null
};
}