Improve session import diagnostics

This commit is contained in:
meoacgx committed 2025-12-20 07:52:01 +08:00
1 parent 8ef277bb78
commit 0acfc0ffdb
6 files changed
+316 -54

No files matched your search

@@ -332,18 +332,18 @@ public class AccountImportService
using var doc = System.Text.Json.JsonDocument.Parse(json);
var root = doc.RootElement;
if (!TryGetInt(root, out var apiId, "api_id", "app_id"))
if (!TryGetInt(root, out var apiId, "api_id", "app_id", "apiId", "appId"))
return new ImportResult(false, null, null, null, null, $"json 缺少 api_id: {jsonPath}");
if (!TryGetString(root, out var apiHash, "api_hash", "app_hash") || string.IsNullOrWhiteSpace(apiHash))
if (!TryGetString(root, out var apiHash, "api_hash", "app_hash", "apiHash", "appHash") || string.IsNullOrWhiteSpace(apiHash))
return new ImportResult(false, null, null, null, null, $"json 缺少 api_hash: {jsonPath}");
if (!TryGetString(root, out var phone, "phone") || string.IsNullOrWhiteSpace(phone))
if (!TryGetString(root, out var phone, "phone", "phone_number", "phoneNumber") || string.IsNullOrWhiteSpace(phone))
return new ImportResult(false, null, null, null, null, $"json 缺少 phone: {jsonPath}");
phone = phone.Trim();
_ = TryGetLong(root, out var userId, "user_id", "uid");
_ = TryGetLong(root, out var userId, "user_id", "uid", "userId");
_ = TryGetString(root, out var username, "username");
username = string.IsNullOrWhiteSpace(username) ? null : username.Trim();
_ = TryGetString(root, out var firstName, "first_name", "firstName");
@@ -374,11 +374,11 @@ public class AccountImportService
// 优先尝试使用 json 里的 session_string(映射到 WTelegram 的 session_key)来生成可用 session 文件。
if (LooksLikeSqliteSession(sessionCandidate))
{
bool ok;
SessionDataConverter.SessionConvertResult converted;
if (string.IsNullOrWhiteSpace(sessionKey))
{
// 没有 session_string 也不阻挡:直接从 sqlite 里取 dc/auth_key 转换为 WTelegram session
ok = await SessionDataConverter.TryCreateWTelegramSessionFromTelethonSqliteFileAsync(
converted = await SessionDataConverter.TryCreateWTelegramSessionFromTelethonSqliteFileAsync(
sqliteSessionPath: sessionCandidate,
apiId: apiId,
apiHash: apiHash.Trim(),
@@ -389,7 +389,7 @@ public class AccountImportService
}
else
{
ok = await SessionDataConverter.TryCreateWTelegramSessionFromSessionStringAsync(
converted = await SessionDataConverter.TryCreateWTelegramSessionFromSessionStringAsync(
sessionString: sessionKey,
apiId: apiId,
apiHash: apiHash.Trim(),
@@ -399,9 +399,11 @@ public class AccountImportService
logger: _logger);
}
if (!ok)
if (!converted.Ok)
{
return new ImportResult(false, phone, userId, username, null, "该 .session 为 SQLite 格式,且无法自动转换为可用 session(请重新登录生成新 session)");
var reason = converted.Reason ?? "未知原因";
return new ImportResult(false, phone, userId, username, null,
$"该 .session 为 SQLite 格式,但转换/校验失败:{reason}(通常表示账号已掉线/被登出/会话失效,需要重新登录生成新 session)");
}
}
else
@@ -705,7 +705,7 @@ public class AccountTelegramToolsService
{
cancellationToken.ThrowIfCancellationRequested();
var ok = await SessionDataConverter.TryConvertSqliteSessionFromJsonAsync(
var converted = await SessionDataConverter.TryConvertSqliteSessionFromJsonAsync(
phone: account.Phone,
apiId: account.ApiId,
apiHash: account.ApiHash,
@@ -713,11 +713,11 @@ public class AccountTelegramToolsService
logger: _logger
);
if (!ok)
if (!converted.Ok)
{
throw new InvalidOperationException(
$"该账号的 Session 文件为 SQLite 格式:{account.SessionPath},无法自动转换为可用 session。" +
"建议:重新导入包含 session_string 的 json,或到【账号-手机号登录】重新登录生成新的 sessions/*.session。");
$"原因:{converted.Reason}。建议:重新导入包含 session_string 的 json,或到【账号-手机号登录】重新登录生成新的 sessions/*.session。");
}
}
@@ -576,7 +576,7 @@ public class ChannelService : IChannelService
var absoluteSessionPath = Path.GetFullPath(account.SessionPath);
if (File.Exists(absoluteSessionPath) && LooksLikeSqliteSession(absoluteSessionPath))
{
var ok = await SessionDataConverter.TryConvertSqliteSessionFromJsonAsync(
var converted = await SessionDataConverter.TryConvertSqliteSessionFromJsonAsync(
phone: account.Phone,
apiId: account.ApiId,
apiHash: account.ApiHash,
@@ -584,12 +584,12 @@ public class ChannelService : IChannelService
logger: _logger
);
if (!ok)
if (!converted.Ok)
{
throw new InvalidOperationException(
$"该账号的 Session 文件为 SQLite 格式:{account.SessionPath},本项目无法直接复用。" +
"已尝试从本地 json(例如 sessions/<手机号>.json 或 session数据/<手机号>/*.json)读取 session_string 自动转换但失败;" +
"请到【账号-手机号登录】重新登录生成新的 sessions/*.session 后再操作。");
$"原因:{converted.Reason}。请到【账号-手机号登录】重新登录生成新的 sessions/*.session 后再操作。");
}
}
@@ -272,7 +272,7 @@ public class GroupService : IGroupService
var absoluteSessionPath = Path.GetFullPath(account.SessionPath);
if (System.IO.File.Exists(absoluteSessionPath) && LooksLikeSqliteSession(absoluteSessionPath))
{
var ok = await SessionDataConverter.TryConvertSqliteSessionFromJsonAsync(
var converted = await SessionDataConverter.TryConvertSqliteSessionFromJsonAsync(
phone: account.Phone,
apiId: account.ApiId,
apiHash: account.ApiHash,
@@ -280,12 +280,12 @@ public class GroupService : IGroupService
logger: _logger
);
if (!ok)
if (!converted.Ok)
{
throw new InvalidOperationException(
$"该账号的 Session 文件为 SQLite 格式:{account.SessionPath},本项目无法直接复用。" +
"已尝试从本地 json(例如 sessions/<手机号>.json 或 session数据/<手机号>/*.json)读取 session_string 自动转换但失败;" +
"请到【账号-手机号登录】重新登录生成新的 sessions/*.session 后再操作。");
$"原因:{converted.Reason}。请到【账号-手机号登录】重新登录生成新的 sessions/*.session 后再操作。");
}
}
@@ -11,9 +11,15 @@ using WTelegram;
namespace TelegramPanel.Core.Services.Telegram;
internal static class SessionDataConverter
public static class SessionDataConverter
{
public static async Task<bool> TryConvertSqliteSessionFromJsonAsync(
public readonly record struct SessionConvertResult(bool Ok, string? Reason)
{
public static SessionConvertResult Success() => new(true, null);
public static SessionConvertResult Fail(string? reason) => new(false, string.IsNullOrWhiteSpace(reason) ? "未知原因" : reason.Trim());
}
public static async Task<SessionConvertResult> TryConvertSqliteSessionFromJsonAsync(
string phone,
int apiId,
string apiHash,
@@ -24,7 +30,7 @@ internal static class SessionDataConverter
{
var absoluteSqliteSessionPath = Path.GetFullPath(sqliteSessionPath);
if (!File.Exists(absoluteSqliteSessionPath) || !LooksLikeSqliteSession(absoluteSqliteSessionPath))
return false;
return SessionConvertResult.Fail("不是有效的 SQLite .session 文件");
var jsonPath = TryFindAnySessionJsonPath(phone, absoluteSqliteSessionPath);
if (!string.IsNullOrWhiteSpace(jsonPath) && File.Exists(jsonPath))
@@ -32,7 +38,12 @@ internal static class SessionDataConverter
var jsonText = await File.ReadAllTextAsync(jsonPath);
using var doc = JsonDocument.Parse(jsonText);
if (doc.RootElement.TryGetProperty("session_string", out var sessionProp) && sessionProp.ValueKind == JsonValueKind.String)
JsonElement sessionProp;
var hasSessionProp = doc.RootElement.TryGetProperty("session_string", out sessionProp);
if (!hasSessionProp || sessionProp.ValueKind != JsonValueKind.String)
hasSessionProp = doc.RootElement.TryGetProperty("sessionString", out sessionProp);
if (hasSessionProp && sessionProp.ValueKind == JsonValueKind.String)
{
var sessionString = sessionProp.GetString();
if (!string.IsNullOrWhiteSpace(sessionString))
@@ -43,7 +54,7 @@ internal static class SessionDataConverter
if (userIdProp.ValueKind == JsonValueKind.Number && userIdProp.TryGetInt64(out var uid1)) userId = uid1;
if (userId == null && uidProp.ValueKind == JsonValueKind.Number && uidProp.TryGetInt64(out var uid2)) userId = uid2;
var ok = await TryCreateWTelegramSessionFromSessionStringAsync(
var converted = await TryCreateWTelegramSessionFromSessionStringAsync(
sessionString: sessionString.Trim(),
apiId: apiId,
apiHash: apiHash,
@@ -52,17 +63,19 @@ internal static class SessionDataConverter
userId: userId,
logger: logger);
if (ok)
if (converted.Ok)
{
logger.LogInformation("Converted sqlite session for {Phone} using json: {JsonPath}", phone, jsonPath);
return true;
return SessionConvertResult.Success();
}
logger.LogWarning("Failed to convert sqlite session for {Phone} using json session_string: {Reason}", phone, converted.Reason);
}
}
}
// json 不存在/缺少 session_string/转换失败 → 直接从 sqlite 读取 dc/auth_key 进行转换
var sqliteOk = await TryCreateWTelegramSessionFromTelethonSqliteFileAsync(
var sqliteConverted = await TryCreateWTelegramSessionFromTelethonSqliteFileAsync(
sqliteSessionPath: absoluteSqliteSessionPath,
apiId: apiId,
apiHash: apiHash,
@@ -71,19 +84,21 @@ internal static class SessionDataConverter
userId: null,
logger: logger);
if (sqliteOk)
if (sqliteConverted.Ok)
logger.LogInformation("Converted sqlite session for {Phone} using sqlite content", phone);
else
logger.LogWarning("Failed to convert sqlite session for {Phone} using sqlite content: {Reason}", phone, sqliteConverted.Reason);
return sqliteOk;
return sqliteConverted;
}
catch (Exception ex)
{
logger.LogWarning(ex, "Failed to convert sqlite session for {Phone} from json", phone);
return false;
return SessionConvertResult.Fail($"{ex.GetType().Name}: {ex.Message}");
}
}
public static async Task<bool> TryCreateWTelegramSessionFromSessionStringAsync(
public static async Task<SessionConvertResult> TryCreateWTelegramSessionFromSessionStringAsync(
string sessionString,
int apiId,
string apiHash,
@@ -96,15 +111,17 @@ internal static class SessionDataConverter
try
{
if (string.IsNullOrWhiteSpace(sessionString))
return false;
return SessionConvertResult.Fail("session_string 为空");
var absoluteTargetSessionPath = Path.GetFullPath(targetSessionPath);
var normalizedPhone = NormalizePhone(phone);
if (string.IsNullOrWhiteSpace(normalizedPhone))
normalizedPhone = NormalizePhone(Path.GetFileNameWithoutExtension(absoluteTargetSessionPath));
if (string.IsNullOrWhiteSpace(normalizedPhone))
return SessionConvertResult.Fail("无法从手机号/文件名解析出 phoneDigits");
if (!TryParseTelethonStringSession(sessionString.Trim(), out var telethon))
return false;
return SessionConvertResult.Fail("session_string 无法解析为 Telethon StringSession(可能格式不兼容或已损坏)");
// 先备份旧 sqlite session,再生成 WTelegram session 覆盖原路径
if (File.Exists(absoluteTargetSessionPath))
@@ -119,7 +136,7 @@ internal static class SessionDataConverter
Directory.CreateDirectory(sessionsDir);
// 使用 WTelegram 的 Session 存储格式生成可用 session 文件(加密 JSON)
var ok = await WriteWTelegramSessionFileAsync(
var written = await WriteWTelegramSessionFileAsync(
apiId: apiId,
apiHash: apiHash,
sessionPath: absoluteTargetSessionPath,
@@ -131,10 +148,10 @@ internal static class SessionDataConverter
authKey: telethon.AuthKey,
logger: logger
);
if (!ok)
throw new InvalidOperationException("WTelegram session 生成失败或校验失败");
if (!written.Ok)
return written;
return true;
return SessionConvertResult.Success();
}
catch (Exception ex)
{
@@ -146,7 +163,7 @@ internal static class SessionDataConverter
File.Move(backupPath, targetSessionPath, overwrite: true);
}
catch { }
return false;
return SessionConvertResult.Fail($"{ex.GetType().Name}: {ex.Message}");
}
}
@@ -167,7 +184,7 @@ internal static class SessionDataConverter
}
}
public static async Task<bool> TryCreateWTelegramSessionFromTelethonSqliteFileAsync(
public static async Task<SessionConvertResult> TryCreateWTelegramSessionFromTelethonSqliteFileAsync(
string sqliteSessionPath,
int apiId,
string apiHash,
@@ -183,14 +200,16 @@ internal static class SessionDataConverter
var absoluteTarget = Path.GetFullPath(targetSessionPath);
if (!File.Exists(absoluteSqlitePath) || !LooksLikeSqliteSession(absoluteSqlitePath))
return false;
return SessionConvertResult.Fail("不是有效的 SQLite .session 文件");
if (!TryReadTelethonSqliteSession(absoluteSqlitePath, out var telethon))
return false;
if (!TryReadTelethonSqliteSession(absoluteSqlitePath, out var telethon, out var readReason))
return SessionConvertResult.Fail($"SQLite session 读取失败:{readReason}");
var normalizedPhone = NormalizePhone(phone);
if (string.IsNullOrWhiteSpace(normalizedPhone))
normalizedPhone = NormalizePhone(Path.GetFileNameWithoutExtension(absoluteTarget));
if (string.IsNullOrWhiteSpace(normalizedPhone))
return SessionConvertResult.Fail("无法从手机号/文件名解析出 phoneDigits");
if (File.Exists(absoluteTarget))
{
@@ -200,7 +219,7 @@ internal static class SessionDataConverter
File.Move(absoluteTarget, backupPath, overwrite: true);
}
var ok = await WriteWTelegramSessionFileAsync(
var written = await WriteWTelegramSessionFileAsync(
apiId: apiId,
apiHash: apiHash,
sessionPath: absoluteTarget,
@@ -213,10 +232,10 @@ internal static class SessionDataConverter
logger: logger
);
if (!ok)
throw new InvalidOperationException("WTelegram session 生成失败或校验失败");
if (!written.Ok)
return written;
return true;
return SessionConvertResult.Success();
}
catch (Exception ex)
{
@@ -228,15 +247,16 @@ internal static class SessionDataConverter
File.Move(backupPath, targetSessionPath, overwrite: true);
}
catch { }
return false;
return SessionConvertResult.Fail($"{ex.GetType().Name}: {ex.Message}");
}
}
private static bool TryReadTelethonSqliteSession(string sqliteSessionPath, out TelethonSessionData data)
private static bool TryReadTelethonSqliteSession(string sqliteSessionPath, out TelethonSessionData data, out string reason)
{
try
{
data = default;
reason = "未知原因";
using var connection = new SqliteConnection(new SqliteConnectionStringBuilder
{
DataSource = sqliteSessionPath,
@@ -248,22 +268,60 @@ internal static class SessionDataConverter
cmd.CommandText = "SELECT dc_id, server_address, port, auth_key FROM sessions LIMIT 1";
using var reader = cmd.ExecuteReader();
if (!reader.Read())
{
reason = "sessions 表为空或无记录";
return false;
}
var dcId = reader.GetInt32(0);
var serverAddress = reader.IsDBNull(1) ? null : reader.GetString(1);
var port = reader.IsDBNull(2) ? 0 : reader.GetInt32(2);
var authKey = reader.IsDBNull(3) ? null : (byte[])reader[3];
if (dcId <= 0 || string.IsNullOrWhiteSpace(serverAddress) || port <= 0 || authKey == null || authKey.Length != 256)
if (dcId <= 0)
{
reason = $"dc_id 无效:{dcId}";
return false;
}
if (string.IsNullOrWhiteSpace(serverAddress))
{
reason = "server_address 为空";
return false;
}
if (port <= 0)
{
reason = $"port 无效:{port}";
return false;
}
if (authKey == null || authKey.Length == 0)
{
reason = "auth_key 为空";
return false;
}
if (authKey.Length != 256)
{
reason = $"auth_key 长度不符合预期:{authKey.Length}(期望 256)";
return false;
}
data = new TelethonSessionData(dcId, serverAddress.Trim(), (ushort)port, authKey);
reason = "OK";
return true;
}
catch
catch (SqliteException ex)
{
data = default;
reason = $"SqliteException: {ex.Message}";
return false;
}
catch (Exception ex)
{
data = default;
reason = $"{ex.GetType().Name}: {ex.Message}";
return false;
}
}
@@ -380,7 +438,7 @@ internal static class SessionDataConverter
}
}
private static async Task<bool> WriteWTelegramSessionFileAsync(
private static async Task<SessionConvertResult> WriteWTelegramSessionFileAsync(
int apiId,
string apiHash,
string sessionPath,
@@ -404,8 +462,9 @@ internal static class SessionDataConverter
};
// 1) 创建空 session 文件
await using (var builder = new Client(Config))
try
{
await using var builder = new Client(Config);
var clientType = typeof(Client);
var sessionField = clientType.GetField("_session", BindingFlags.Instance | BindingFlags.NonPublic)
?? throw new InvalidOperationException("无法访问 WTelegram.Client._session");
@@ -456,6 +515,11 @@ internal static class SessionDataConverter
?? throw new InvalidOperationException("无法访问 Session.Save()");
lock (sessionObj) save.Invoke(sessionObj, null);
}
catch (Exception ex)
{
logger.LogWarning(ex, "Failed to write WTelegram session file for {Phone}", phoneDigits);
return SessionConvertResult.Fail($"WTelegram session 文件写入失败:{ex.GetType().Name}: {ex.Message}");
}
// 2) 验证:使用已导入的 AuthKey 直接请求 Self(避免进入 LoginUserIfNeeded 的“发验证码”分支)
await using var probe = new Client(Config);
@@ -465,7 +529,7 @@ internal static class SessionDataConverter
var users = await probe.Users_GetUsers(InputUser.Self);
var self = users.OfType<User>().FirstOrDefault();
if (self == null)
return false;
return SessionConvertResult.Fail("已连接但无法获取 Self(可能 session 未授权/已失效)");
// 写回 UserId(让后续服务端能用 LoginUserIfNeeded 快速恢复 User)
var clientType = typeof(Client);
@@ -486,12 +550,17 @@ internal static class SessionDataConverter
lock (sessionObj) save.Invoke(sessionObj, null);
logger.LogInformation("WTelegram session validated for {Phone} (user_id={UserId}) on DC {DcId}", phoneDigits, self.id, dcId);
return true;
return SessionConvertResult.Success();
}
catch (RpcException ex)
{
logger.LogWarning(ex, "WTelegram session validation failed for {Phone}", phoneDigits);
return SessionConvertResult.Fail($"Telegram RPC 错误:{ex.Message}");
}
catch (Exception ex)
{
logger.LogWarning(ex, "WTelegram session validation failed for {Phone}", phoneDigits);
return false;
return SessionConvertResult.Fail($"验证失败:{ex.GetType().Name}: {ex.Message}");
}
}
+191
View File
@@ -10,6 +10,197 @@ using TelegramPanel.Core.Services;
using TelegramPanel.Core.Services.Telegram;
using TelegramPanel.Data;
using TelegramPanel.Web.Services;
using Microsoft.Extensions.Logging;
// 诊断:对某个目录下的 *.json/*.session 做一次“可转换/可校验”检查(不写数据库)
// 用法:dotnet run --project src/TelegramPanel.Web -- --diag-session-dir "D:/path/to/dir"
if (args.Length >= 2 && string.Equals(args[0], "--diag-session-dir", StringComparison.OrdinalIgnoreCase))
{
var dir = args[1];
if (!Directory.Exists(dir))
{
Console.Error.WriteLine($"目录不存在:{dir}");
return;
}
using var loggerFactory = Microsoft.Extensions.Logging.LoggerFactory.Create(b =>
{
b.AddSimpleConsole(o =>
{
o.SingleLine = true;
o.TimestampFormat = "HH:mm:ss ";
});
b.SetMinimumLevel(Microsoft.Extensions.Logging.LogLevel.Information);
});
var logger = loggerFactory.CreateLogger("SessionDiag");
var jsonFiles = Directory.EnumerateFiles(dir, "*.json", SearchOption.TopDirectoryOnly)
.OrderBy(p => p, StringComparer.OrdinalIgnoreCase)
.ToList();
if (jsonFiles.Count == 0)
{
Console.Error.WriteLine("目录内未找到任何 .json 文件");
return;
}
var tempOutDir = Path.Combine(Path.GetTempPath(), "telegram-panel-diag-sessions");
Directory.CreateDirectory(tempOutDir);
foreach (var jsonPath in jsonFiles)
{
try
{
var json = await File.ReadAllTextAsync(jsonPath);
using var doc = System.Text.Json.JsonDocument.Parse(json);
var root = doc.RootElement;
if (!TryGetInt(root, out var apiId, "api_id", "app_id"))
{
Console.WriteLine($"[FAIL] {Path.GetFileName(jsonPath)}: 缺少 api_id/app_id");
continue;
}
if (!TryGetString(root, out var apiHash, "api_hash", "app_hash") || string.IsNullOrWhiteSpace(apiHash))
{
Console.WriteLine($"[FAIL] {Path.GetFileName(jsonPath)}: 缺少 api_hash/app_hash");
continue;
}
if (!TryGetString(root, out var phone, "phone") || string.IsNullOrWhiteSpace(phone))
{
Console.WriteLine($"[FAIL] {Path.GetFileName(jsonPath)}: 缺少 phone");
continue;
}
_ = TryGetLong(root, out var userId, "user_id", "uid");
_ = TryGetString(root, out var sessionString, "session_string", "sessionString");
phone = phone.Trim();
apiHash = apiHash.Trim();
sessionString = string.IsNullOrWhiteSpace(sessionString) ? null : sessionString.Trim();
var baseName = Path.GetFileNameWithoutExtension(jsonPath);
var sessionPath = Path.Combine(dir, $"{baseName}.session");
if (!File.Exists(sessionPath))
sessionPath = Directory.EnumerateFiles(dir, "*.session", SearchOption.TopDirectoryOnly).FirstOrDefault() ?? string.Empty;
if (string.IsNullOrWhiteSpace(sessionPath) || !File.Exists(sessionPath))
{
Console.WriteLine($"[FAIL] {phone}: 未找到 .session 文件(json={Path.GetFileName(jsonPath)})");
continue;
}
var targetSessionPath = Path.Combine(tempOutDir, $"{phone}.session");
TelegramPanel.Core.Services.Telegram.SessionDataConverter.SessionConvertResult converted;
if (TelegramPanel.Core.Services.Telegram.SessionDataConverter.LooksLikeSqliteSession(sessionPath))
{
if (!string.IsNullOrWhiteSpace(sessionString))
{
converted = await TelegramPanel.Core.Services.Telegram.SessionDataConverter.TryCreateWTelegramSessionFromSessionStringAsync(
sessionString: sessionString,
apiId: apiId,
apiHash: apiHash,
targetSessionPath: targetSessionPath,
phone: phone,
userId: userId,
logger: logger);
}
else
{
converted = await TelegramPanel.Core.Services.Telegram.SessionDataConverter.TryCreateWTelegramSessionFromTelethonSqliteFileAsync(
sqliteSessionPath: sessionPath,
apiId: apiId,
apiHash: apiHash,
targetSessionPath: targetSessionPath,
phone: phone,
userId: userId,
logger: logger);
}
}
else
{
File.Copy(sessionPath, targetSessionPath, overwrite: true);
converted = TelegramPanel.Core.Services.Telegram.SessionDataConverter.SessionConvertResult.Success();
}
if (converted.Ok)
Console.WriteLine($"[OK] {phone}: 可用(输出={targetSessionPath})");
else
Console.WriteLine($"[FAIL] {phone}: {converted.Reason}");
}
catch (Exception ex)
{
Console.WriteLine($"[FAIL] {Path.GetFileName(jsonPath)}: {ex.GetType().Name}: {ex.Message}");
}
}
return;
static bool TryGetInt(System.Text.Json.JsonElement root, out int value, params string[] names)
{
foreach (var name in names)
{
if (root.TryGetProperty(name, out var prop))
{
if (prop.ValueKind == System.Text.Json.JsonValueKind.Number && prop.TryGetInt32(out var i))
{
value = i;
return true;
}
if (prop.ValueKind == System.Text.Json.JsonValueKind.String && int.TryParse(prop.GetString(), out var isv))
{
value = isv;
return true;
}
}
}
value = 0;
return false;
}
static bool TryGetLong(System.Text.Json.JsonElement root, out long? value, params string[] names)
{
foreach (var name in names)
{
if (root.TryGetProperty(name, out var prop))
{
if (prop.ValueKind == System.Text.Json.JsonValueKind.Number && prop.TryGetInt64(out var l))
{
value = l;
return true;
}
if (prop.ValueKind == System.Text.Json.JsonValueKind.String && long.TryParse(prop.GetString(), out var ls))
{
value = ls;
return true;
}
}
}
value = null;
return false;
}
static bool TryGetString(System.Text.Json.JsonElement root, out string? value, params string[] names)
{
foreach (var name in names)
{
if (root.TryGetProperty(name, out var prop) && prop.ValueKind == System.Text.Json.JsonValueKind.String)
{
value = prop.GetString();
return true;
}
}
value = null;
return false;
}
}
var builder = WebApplication.CreateBuilder(args);