功能:Zip导入支持SQLite会话时尝试使用session_string转换

This commit is contained in:
meoacgx committed 2025-12-19 11:23:29 +08:00
1 parent 74cef38392
commit 985d4ece3f
1 file changed
+73 -1
@@ -5,6 +5,7 @@ using TelegramPanel.Core.Interfaces;
using TelegramPanel.Core.Services;
using TelegramPanel.Data.Entities;
using System.IO.Compression;
using WTelegram;
namespace TelegramPanel.Core.Services.Telegram;
@@ -317,6 +318,8 @@ public class AccountImportService
_ = TryGetString(root, out var firstName, "first_name", "firstName");
_ = TryGetString(root, out var lastName, "last_name", "lastName");
var nickname = BuildNickname(firstName, lastName, username);
_ = TryGetString(root, out var sessionKey, "session_string", "sessionString");
sessionKey = string.IsNullOrWhiteSpace(sessionKey) ? null : sessionKey.Trim();
var dir = Path.GetDirectoryName(jsonPath) ?? extractDirFallback();
var baseName = Path.GetFileNameWithoutExtension(jsonPath);
@@ -335,7 +338,33 @@ public class AccountImportService
var sessionsPath = _configuration["Telegram:SessionsPath"] ?? "sessions";
Directory.CreateDirectory(sessionsPath);
var targetSessionPath = Path.Combine(sessionsPath, $"{phone}.session");
File.Copy(sessionCandidate, targetSessionPath, overwrite: true);
// 有些来源的 .session 实际上是 SQLite(Telethon/Pyrogram/Telegram Desktop 等),WTelegram 不能直接读取。
// 优先尝试使用 json 里的 session_string(映射到 WTelegram 的 session_key)来生成可用 session 文件。
if (LooksLikeSqliteSession(sessionCandidate))
{
if (string.IsNullOrWhiteSpace(sessionKey))
{
return new ImportResult(false, phone, userId, username, null, "该 .session 为 SQLite 格式且缺少 session_string,无法导入(请重新登录生成新 session)");
}
var ok = await TryCreateSessionFromSessionKeyAsync(
apiId: apiId,
apiHash: apiHash.Trim(),
sessionPath: targetSessionPath,
sessionKey: sessionKey,
userId: userId
);
if (!ok)
{
return new ImportResult(false, phone, userId, username, null, "该 .session 为 SQLite 格式,且 session_string 无法转换为可用 session(请重新登录生成新 session)");
}
}
else
{
File.Copy(sessionCandidate, targetSessionPath, overwrite: true);
}
// 入库:存在则更新,不存在则创建
var existing = await _accountManagement.GetAccountByPhoneAsync(phone);
@@ -392,6 +421,49 @@ public class AccountImportService
return string.IsNullOrWhiteSpace(username) ? null : username.Trim();
}
private static bool LooksLikeSqliteSession(string filePath)
{
try
{
using var fs = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
Span<byte> header = stackalloc byte[16];
var read = fs.Read(header);
if (read < 15) return false;
var text = System.Text.Encoding.ASCII.GetString(header[..15]);
return string.Equals(text, "SQLite format 3", StringComparison.Ordinal);
}
catch
{
return false;
}
}
private async Task<bool> TryCreateSessionFromSessionKeyAsync(int apiId, string apiHash, string sessionPath, string sessionKey, long? userId)
{
try
{
string Config(string what) => what switch
{
"api_id" => apiId.ToString(),
"api_hash" => apiHash,
"session_pathname" => sessionPath,
"session_key" => sessionKey,
"user_id" => userId?.ToString() ?? null!,
_ => null!
};
using var client = new Client(Config);
await client.ConnectAsync();
return client.User != null;
}
catch (Exception ex)
{
_logger.LogWarning(ex, "Failed to create session from session_key");
try { if (File.Exists(sessionPath)) File.Delete(sessionPath); } catch { }
return false;
}
}
private static bool TryGetString(System.Text.Json.JsonElement root, out string? value, params string[] names)
{
foreach (var name in names)