手机号登录增强:规范化手机号并支持重发验证码

This commit is contained in:
meoacgx committed 2025-12-19 16:26:08 +08:00
1 parent e4cfab891b
commit 8c2499e988
3 files changed
+88 -8

No files matched your search

@@ -17,6 +17,11 @@ public interface IAccountService
/// </summary>
Task<LoginResult> SubmitCodeAsync(int accountId, string code);
/// <summary>
/// 重新发送验证码(可能切换到短信/电话等其它通道,取决于 Telegram 策略)
/// </summary>
Task<LoginResult> ResendCodeAsync(int accountId);
/// <summary>
/// 提交两步验证密码
/// </summary>
@@ -36,20 +36,21 @@ public class AccountService : IAccountService
var apiHash = _configuration["Telegram:ApiHash"]!.Trim();
var sessionsPath = _configuration["Telegram:SessionsPath"] ?? "sessions";
Directory.CreateDirectory(sessionsPath);
var sessionPath = Path.Combine(sessionsPath, $"{phone}.session");
var normalizedPhone = NormalizePhoneForLogin(phone);
var sessionPath = Path.Combine(sessionsPath, $"{normalizedPhone}.session");
// 若已有旧的 SQLite 格式 session(Telethon/Pyrogram/Telegram Desktop 常见),会导致 WTelegramClient 直接读取失败,
// 这里先自动备份,确保“手机号登录”能顺利重新生成 WTelegram 的 session。
TryBackupSqliteSessionIfExists(sessionPath);
_logger.LogInformation("Starting login for phone {Phone}", phone);
_logger.LogInformation("Starting login for phone {Phone}", normalizedPhone);
var client = await _clientPool.GetOrCreateClientAsync(accountId, apiId, apiHash, sessionPath, sessionKey: apiHash, phoneNumber: phone, userId: null);
var client = await _clientPool.GetOrCreateClientAsync(accountId, apiId, apiHash, sessionPath, sessionKey: apiHash, phoneNumber: normalizedPhone, userId: null);
string result;
try
{
result = await client.Login(phone);
result = await client.Login(normalizedPhone);
}
catch (Exception ex) when (LooksLikeSessionApiMismatchOrCorrupted(ex))
{
@@ -57,20 +58,42 @@ public class AccountService : IAccountService
TryBackupCorruptedSessionIfExists(sessionPath);
await _clientPool.RemoveClientAsync(accountId);
client = await _clientPool.GetOrCreateClientAsync(accountId, apiId, apiHash, sessionPath, sessionKey: apiHash, phoneNumber: phone, userId: null);
result = await client.Login(phone);
client = await _clientPool.GetOrCreateClientAsync(accountId, apiId, apiHash, sessionPath, sessionKey: apiHash, phoneNumber: normalizedPhone, userId: null);
result = await client.Login(normalizedPhone);
}
_logger.LogInformation("Login flow next step for {Phone}: {Step}", normalizedPhone, result);
return result switch
{
"verification_code" => new LoginResult(false, "code", "请输入验证码"),
"password" => new LoginResult(false, "password", "请输入两步验证密码"),
"name" => new LoginResult(false, "signup", "需要注册新账号"),
"email" => new LoginResult(false, "email", "该账号需要邮箱验证(请按提示填写邮箱并完成验证)"),
"email_verification_code" => new LoginResult(false, "email_code", "请输入邮箱验证码"),
_ when client.User != null => new LoginResult(true, null, "登录成功", MapToAccountInfo(accountId, client)),
_ => new LoginResult(false, null, $"未知状态: {result}")
};
}
public async Task<LoginResult> ResendCodeAsync(int accountId)
{
var client = _clientPool.GetClient(accountId)
?? throw new InvalidOperationException($"Client not found for account {accountId}");
// WTelegram 约定:verification_code 提交空字符串会触发“通过另一种方式重发验证码”(短信/电话等)
var result = await client.Login(string.Empty);
_logger.LogInformation("Resend code requested for temp account {AccountId}, next step: {Step}", accountId, result);
return result switch
{
"verification_code" => new LoginResult(false, "code", "已请求重新发送验证码"),
"password" => new LoginResult(false, "password", "需要两步验证密码"),
_ when client.User != null => new LoginResult(true, null, "登录成功", MapToAccountInfo(accountId, client)),
_ => new LoginResult(false, null, $"重新发送失败:{result}")
};
}
private static bool LooksLikeSessionApiMismatchOrCorrupted(Exception ex)
{
var msg = ex.Message ?? string.Empty;
@@ -146,6 +169,7 @@ public class AccountService : IAccountService
var client = _clientPool.GetClient(accountId)
?? throw new InvalidOperationException($"Client not found for account {accountId}");
code = (code ?? string.Empty).Trim();
var result = await client.Login(code);
return result switch
@@ -221,4 +245,26 @@ public class AccountService : IAccountService
LastActiveAt = DateTime.UtcNow
};
}
private static string NormalizePhoneForLogin(string phone)
{
phone = (phone ?? string.Empty).Trim();
if (phone.StartsWith("+", StringComparison.Ordinal))
phone = phone[1..];
if (phone.StartsWith("00", StringComparison.Ordinal))
phone = phone[2..];
Span<char> buf = stackalloc char[phone.Length];
var n = 0;
foreach (var ch in phone)
{
if (ch is >= '0' and <= '9')
buf[n++] = ch;
}
if (n == 0)
throw new ArgumentException("手机号格式不正确,请包含国家代码(例如:+8613800138000)", nameof(phone));
return new string(buf[..n]);
}
}
@@ -45,8 +45,12 @@
<MudTextField @bind-Value="verificationCode" Label="验证码" Variant="Variant.Outlined" Required="true"
Placeholder="12345" />
<MudText Typo="Typo.caption" Class="mt-2" Color="Color.Info">
请在Telegram客户端查收验证码
说明:验证码通常优先发送到已登录设备的 Telegram「系统通知(777000)」;没有可用设备时才可能发送短信/电话。
</MudText>
<MudButton Variant="Variant.Outlined" Color="Color.Secondary" Class="mt-3"
Disabled="@logging" OnClick="@(async (MouseEventArgs _) => await ResendCodeAsync())">
重新发送验证码(尝试切换通道)
</MudButton>
</MudStep>
<MudStep Title="密码验证" Optional="true">
@@ -173,7 +177,7 @@
}
// 生成临时账号ID(用于跟踪登录过程)
accountId = DateTime.UtcNow.GetHashCode();
accountId = Random.Shared.Next(1, int.MaxValue);
var result = await AccountService.StartLoginAsync(accountId, phoneNumber);
@@ -248,6 +252,31 @@
}
}
private async Task ResendCodeAsync()
{
if (logging) return;
if (accountId == 0)
{
Snackbar.Add("请先在上一步发送验证码", Severity.Warning);
return;
}
logging = true;
try
{
var result = await AccountService.ResendCodeAsync(accountId);
Snackbar.Add(result.Message ?? "已请求重新发送验证码", result.Success ? Severity.Success : Severity.Info);
}
catch (Exception ex)
{
Snackbar.Add($"重新发送失败:{ex.Message}", Severity.Error);
}
finally
{
logging = false;
}
}
private async Task SaveAccount(TelegramPanel.Core.Models.AccountInfo accountInfo)
{
try