fix(export): 导出 tdata 时优先选择已授权 DCSession,避免生成未登录会话

- 导出 Telethon session_string 时改为多候选打分:优先 _dcSession、UserId>0、匹配目标 userId、匹配 MainDC\n- 新增 DCSession 解析辅助方法,避免误取未授权 key\n- 保留 base64 padding 输出,确保 auth key 长度稳定为 256\n- 增加导出日志,便于定位 DC 选择结果
This commit is contained in:
meoacgx committed 2026-03-03 00:25:44 +08:00
1 parent 8c9a3d5a60
commit 4947f7a9ad
1 file changed
+98 -21
@@ -479,6 +479,7 @@ public static class SessionDataConverter
using var client = new Client(Config);
var clientType = typeof(Client);
var currentDcSessionField = clientType.GetField("_dcSession", BindingFlags.Instance | BindingFlags.NonPublic);
var sessionField = clientType.GetField("_session", BindingFlags.Instance | BindingFlags.NonPublic)
?? throw new InvalidOperationException("无法访问 WTelegram.Client._session");
var sessionObj = sessionField.GetValue(client) ?? throw new InvalidOperationException("WTelegram session 未初始化");
@@ -490,33 +491,55 @@ public static class SessionDataConverter
if (dcSessions == null || dcSessions.Count == 0)
return TelethonStringSessionResult.Fail("session 中未找到任何 DC 会话数据");
object? dcSessionObj = null;
if (mainDc > 0 && dcSessions.Contains(mainDc))
dcSessionObj = dcSessions[mainDc];
var candidateSessions = new List<(object DcSessionObj, bool IsCurrentDc)>();
var currentDcSessionObj = currentDcSessionField?.GetValue(client);
if (currentDcSessionObj != null)
candidateSessions.Add((currentDcSessionObj, true));
if (dcSessionObj == null)
if (mainDc > 0 && dcSessions.Contains(mainDc) && dcSessions[mainDc] != null)
candidateSessions.Add((dcSessions[mainDc]!, false));
foreach (System.Collections.DictionaryEntry entry in dcSessions)
{
foreach (System.Collections.DictionaryEntry entry in dcSessions)
{
if (entry.Value != null)
{
dcSessionObj = entry.Value;
break;
}
}
if (entry.Value != null)
candidateSessions.Add((entry.Value, false));
}
if (dcSessionObj == null)
return TelethonStringSessionResult.Fail("无法从 session 中解析出 DCSession");
byte[]? authKey = null;
DcOption? dataCenterObj = null;
long pickedDcUserId = 0;
var bestScore = int.MinValue;
var dcSessionType = dcSessionObj.GetType();
var authKey = dcSessionType.GetField("AuthKey")?.GetValue(dcSessionObj) as byte[];
if (authKey == null || authKey.Length != 256)
return TelethonStringSessionResult.Fail($"AuthKey 无效(长度:{authKey?.Length ?? 0},期望 256)");
foreach (var (dcSessionObj, isCurrentDc) in candidateSessions)
{
if (!TryExtractDcSessionInfo(dcSessionObj, out var candidateAuthKey, out var candidateDataCenter, out var dcUserId))
continue;
if (candidateAuthKey == null || candidateAuthKey.Length != 256)
continue;
if (candidateDataCenter == null)
continue;
var dataCenterObj = dcSessionType.GetField("DataCenter")?.GetValue(dcSessionObj) as DcOption;
if (dataCenterObj == null)
return TelethonStringSessionResult.Fail("无法从 session 中解析 DataCenter");
var score = 0;
if (isCurrentDc)
score += 100;
if (dcUserId > 0)
score += 50;
if (userId.HasValue && userId.Value > 0 && dcUserId == userId.Value)
score += 40;
if (mainDc > 0 && candidateDataCenter.id == mainDc)
score += 20;
if (score <= bestScore)
continue;
bestScore = score;
authKey = candidateAuthKey;
dataCenterObj = candidateDataCenter;
pickedDcUserId = dcUserId;
}
if (authKey == null || authKey.Length != 256 || dataCenterObj == null)
return TelethonStringSessionResult.Fail("无法从 session 中解析有效的已授权 DCSession(AuthKey/DataCenter)");
if (dataCenterObj.id <= 0 || dataCenterObj.id > byte.MaxValue)
return TelethonStringSessionResult.Fail($"DataCenter ID 无效:{dataCenterObj.id}");
@@ -541,6 +564,13 @@ public static class SessionDataConverter
Buffer.BlockCopy(authKey, 0, packed, 1 + ipBytes.Length + 2, 256);
var sessionString = "1" + Base64UrlEncode(packed);
logger.LogInformation(
"Picked DCSession for Telethon export: mainDc={MainDc}, dcId={DcId}, dcUserId={DcUserId}, targetUserId={TargetUserId}, isIpV6={IsIpV6}",
mainDc,
dataCenterObj.id,
pickedDcUserId,
userId,
ipBytes.Length == 16);
return TelethonStringSessionResult.Success(sessionString);
}
catch (Exception ex)
@@ -550,6 +580,53 @@ public static class SessionDataConverter
}
}
private static bool TryExtractDcSessionInfo(
object dcSessionObj,
out byte[]? authKey,
out DcOption? dataCenter,
out long dcUserId)
{
try
{
var dcSessionType = dcSessionObj.GetType();
authKey = dcSessionType.GetField("AuthKey")?.GetValue(dcSessionObj) as byte[];
dataCenter = dcSessionType.GetField("DataCenter")?.GetValue(dcSessionObj) as DcOption;
dcUserId = 0;
var userIdObj = dcSessionType.GetField("UserId")?.GetValue(dcSessionObj);
switch (userIdObj)
{
case int i:
dcUserId = i;
break;
case long l:
dcUserId = l;
break;
case uint ui:
dcUserId = ui;
break;
case ulong ul:
dcUserId = (long)Math.Min(ul, long.MaxValue);
break;
case short s:
dcUserId = s;
break;
case ushort us:
dcUserId = us;
break;
}
return authKey != null && dataCenter != null;
}
catch
{
authKey = null;
dataCenter = null;
dcUserId = 0;
return false;
}
}
private static async Task<SessionConvertResult> WriteWTelegramSessionFileAsync(
int apiId,
string apiHash,