diff --git a/Directory.Build.props b/Directory.Build.props
index 8a5271f..c2b7135 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -5,10 +5,10 @@
- 1.2.4
- 1.2.4.0
- 1.2.4.0
- 1.2.4
+ 1.2.5
+ 1.2.5.0
+ 1.2.5.0
+ 1.2.5
Telegram Panel Contributors
diff --git a/src/TelegramPanel.Core/Services/AccountManagementService.cs b/src/TelegramPanel.Core/Services/AccountManagementService.cs
index cd9758e..5539dac 100644
--- a/src/TelegramPanel.Core/Services/AccountManagementService.cs
+++ b/src/TelegramPanel.Core/Services/AccountManagementService.cs
@@ -63,9 +63,10 @@ public class AccountManagementService
string? search,
int pageIndex,
int pageSize,
+ bool onlyWaste = false,
CancellationToken cancellationToken = default)
{
- var (items, total) = await _accountRepository.QueryPagedAsync(categoryId, search, pageIndex, pageSize, cancellationToken);
+ var (items, total) = await _accountRepository.QueryPagedAsync(categoryId, search, pageIndex, pageSize, onlyWaste, cancellationToken);
foreach (var a in items)
FormatPhoneForDisplay(a);
return (items, total);
@@ -74,9 +75,10 @@ public class AccountManagementService
public async Task> QueryAccountsAsync(
int? categoryId,
string? search,
+ bool onlyWaste = false,
CancellationToken cancellationToken = default)
{
- var list = await _accountRepository.QueryAsync(categoryId, search, cancellationToken);
+ var list = await _accountRepository.QueryAsync(categoryId, search, onlyWaste, cancellationToken);
foreach (var a in list)
FormatPhoneForDisplay(a);
return list;
diff --git a/src/TelegramPanel.Core/Services/Telegram/AccountTelegramToolsService.cs b/src/TelegramPanel.Core/Services/Telegram/AccountTelegramToolsService.cs
index ad76e23..441aca7 100644
--- a/src/TelegramPanel.Core/Services/Telegram/AccountTelegramToolsService.cs
+++ b/src/TelegramPanel.Core/Services/Telegram/AccountTelegramToolsService.cs
@@ -1047,6 +1047,22 @@ public class AccountTelegramToolsService
{
var msg = ex.Message ?? string.Empty;
+ if (msg.Contains("EMAIL_HASH_EXPIRED", StringComparison.OrdinalIgnoreCase))
+ return (
+ "邮箱验证码已过期(EMAIL_HASH_EXPIRED)",
+ "请点击“重发验证码”,并使用最新邮件中的验证码。" + Environment.NewLine + msg);
+
+ if (msg.Contains("EMAIL_UNCONFIRMED", StringComparison.OrdinalIgnoreCase))
+ return (
+ "邮箱未确认(EMAIL_UNCONFIRMED)",
+ "请在面板输入邮箱收到的验证码进行确认;如提示过期请重发并使用最新验证码。" + Environment.NewLine + msg);
+
+ if (msg.Contains("EMAIL_INVALID", StringComparison.OrdinalIgnoreCase))
+ return ("邮箱无效(EMAIL_INVALID)", msg);
+
+ if (msg.Contains("EMAIL_NOT_ALLOWED", StringComparison.OrdinalIgnoreCase))
+ return ("邮箱不允许使用(EMAIL_NOT_ALLOWED)", msg);
+
if (msg.Contains("FROZEN_METHOD_INVALID", StringComparison.OrdinalIgnoreCase))
return ("账号被冻结(FROZEN_METHOD_INVALID)", "Telegram 提示该账号/ApiId 的某些接口被冻结(常见为创建频道接口)。" + Environment.NewLine + msg);
diff --git a/src/TelegramPanel.Data/Repositories/AccountRepository.cs b/src/TelegramPanel.Data/Repositories/AccountRepository.cs
index b157279..7a12649 100644
--- a/src/TelegramPanel.Data/Repositories/AccountRepository.cs
+++ b/src/TelegramPanel.Data/Repositories/AccountRepository.cs
@@ -12,7 +12,7 @@ public class AccountRepository : Repository, IAccountRepository
{
}
- private IQueryable BuildQuery(int? categoryId, string? search)
+ private IQueryable BuildQuery(int? categoryId, string? search, bool onlyWaste)
{
var query = _dbSet
.AsNoTracking()
@@ -48,6 +48,44 @@ public class AccountRepository : Repository, IAccountRepository
}
}
+ if (onlyWaste)
+ {
+ // 仅废号:基于“上次 Telegram 状态检测摘要(Summary)”做筛选。
+ // 注意:未检测的账号(Summary 为空)不会被包含。
+ query = query.Where(a =>
+ a.TelegramStatusSummary != null
+ && (
+ // 账号封禁/停用
+ EF.Functions.Like(a.TelegramStatusSummary, "%账号被封禁%")
+ || EF.Functions.Like(a.TelegramStatusSummary, "%被封禁/停用%")
+ || EF.Functions.Like(a.TelegramStatusSummary, "%USER_DEACTIVATED%")
+ || EF.Functions.Like(a.TelegramStatusSummary, "%PHONE_NUMBER_BANNED%")
+
+ // Session 失效/损坏
+ || EF.Functions.Like(a.TelegramStatusSummary, "%Session 失效%")
+ || EF.Functions.Like(a.TelegramStatusSummary, "%AUTH_KEY_UNREGISTERED%")
+ || EF.Functions.Like(a.TelegramStatusSummary, "%Session 无法读取%")
+ || EF.Functions.Like(a.TelegramStatusSummary, "%Can't read session block%")
+
+ // 受限/冻结/未登录
+ || EF.Functions.Like(a.TelegramStatusSummary, "%账号受限%")
+ || EF.Functions.Like(a.TelegramStatusSummary, "%Restricted%")
+ || EF.Functions.Like(a.TelegramStatusSummary, "%账号被冻结%")
+ || EF.Functions.Like(a.TelegramStatusSummary, "%FROZEN_METHOD_INVALID%")
+ || EF.Functions.Like(a.TelegramStatusSummary, "%需要两步验证密码%")
+ || EF.Functions.Like(a.TelegramStatusSummary, "%SESSION_PASSWORD_NEEDED%")
+
+ // 注销/删除
+ || EF.Functions.Like(a.TelegramStatusSummary, "%账号已注销%")
+ || EF.Functions.Like(a.TelegramStatusSummary, "%已注销/被删除%")
+ || EF.Functions.Like(a.TelegramStatusSummary, "%被删除%")
+
+ // 连接/探测失败(按“废号”处理)
+ || EF.Functions.Like(a.TelegramStatusSummary, "%连接失败%")
+ || EF.Functions.Like(a.TelegramStatusSummary, "%创建频道探测失败%")
+ ));
+ }
+
return query.OrderByDescending(a => a.Id);
}
@@ -118,13 +156,14 @@ public class AccountRepository : Repository, IAccountRepository
string? search,
int pageIndex,
int pageSize,
+ bool onlyWaste,
CancellationToken cancellationToken = default)
{
if (pageIndex < 0) pageIndex = 0;
if (pageSize <= 0) pageSize = 20;
if (pageSize > 500) pageSize = 500;
- var query = BuildQuery(categoryId, search);
+ var query = BuildQuery(categoryId, search, onlyWaste);
var total = await query.CountAsync(cancellationToken);
var items = await query
.Skip(pageIndex * pageSize)
@@ -137,9 +176,10 @@ public class AccountRepository : Repository, IAccountRepository
public async Task> QueryAsync(
int? categoryId,
string? search,
+ bool onlyWaste,
CancellationToken cancellationToken = default)
{
- var query = BuildQuery(categoryId, search);
+ var query = BuildQuery(categoryId, search, onlyWaste);
return await query.ToListAsync(cancellationToken);
}
}
diff --git a/src/TelegramPanel.Data/Repositories/IAccountRepository.cs b/src/TelegramPanel.Data/Repositories/IAccountRepository.cs
index 400ef79..2637338 100644
--- a/src/TelegramPanel.Data/Repositories/IAccountRepository.cs
+++ b/src/TelegramPanel.Data/Repositories/IAccountRepository.cs
@@ -17,10 +17,12 @@ public interface IAccountRepository : IRepository
string? search,
int pageIndex,
int pageSize,
+ bool onlyWaste = false,
CancellationToken cancellationToken = default);
Task> QueryAsync(
int? categoryId,
string? search,
+ bool onlyWaste = false,
CancellationToken cancellationToken = default);
}
diff --git a/src/TelegramPanel.Web/Components/Dialogs/ChangeTwoFactorRecoveryEmailDialog.razor b/src/TelegramPanel.Web/Components/Dialogs/ChangeTwoFactorRecoveryEmailDialog.razor
index 0dc8123..33048ef 100644
--- a/src/TelegramPanel.Web/Components/Dialogs/ChangeTwoFactorRecoveryEmailDialog.razor
+++ b/src/TelegramPanel.Web/Components/Dialogs/ChangeTwoFactorRecoveryEmailDialog.razor
@@ -43,18 +43,40 @@
该账号未开启两步验证,无法绑定找回邮箱。请先设置二级密码。
}
- else if (step == Step.SetEmail)
+ else
{
+ 发送验证码
+
- }
- else
- {
+
+
+
+ @if (running)
+ {
+
+ 发送中...
+ }
+ else
+ {
+ 发送验证码
+ }
+
+
+ 提示:即使“发送/重发”提示失败,但你实际收到了邮件验证码,也可以直接在下方输入验证码确认。
+
+
+
+
+
+ 输入验证码确认
+
+ HelperText="请到邮箱中查看验证码(注意垃圾箱/延迟)。如提示过期,请先重发并使用最新验证码。"
+ Required="true" />
@@ -63,6 +85,17 @@
取消验证
+
+ @if (running)
+ {
+
+ 确认中...
+ }
+ else
+ {
+ 确认绑定
+ }
+
}
}
@@ -77,37 +110,7 @@
去设置二级密码
}
- else if (loaded && account != null && hasTwoFactorPassword)
- {
- @if (step == Step.SetEmail)
- {
-
- @if (running)
- {
-
- 发送中...
- }
- else
- {
- 发送验证码
- }
-
- }
- else
- {
-
- @if (running)
- {
-
- 确认中...
- }
- else
- {
- 确认绑定
- }
-
- }
- }
+
@@ -124,7 +127,6 @@
private bool hasRecoveryEmail;
private string? unconfirmedEmailPattern;
- private Step step = Step.SetEmail;
private string currentPassword = "";
private string email = "";
@@ -135,7 +137,7 @@
await ReloadStatus();
}
- private async Task ReloadStatus()
+ private async Task ReloadStatus(bool silent = false)
{
loaded = false;
try
@@ -146,7 +148,7 @@
if (account != null)
{
var (ok, err, hasPwd, hasEmail, unconfirmed) = await AccountTelegramTools.GetTwoFactorRecoveryEmailStatusAsync(AccountId);
- if (!ok)
+ if (!ok && !silent)
{
Snackbar.Add($"获取2FA邮箱状态失败:{err}", Severity.Warning);
}
@@ -154,7 +156,6 @@
hasRecoveryEmail = hasEmail;
unconfirmedEmailPattern = unconfirmed;
- step = string.IsNullOrWhiteSpace(unconfirmedEmailPattern) ? Step.SetEmail : Step.Confirm;
}
}
finally
@@ -203,11 +204,17 @@
if (!ok)
{
Snackbar.Add($"发送失败:{err}", Severity.Error);
+ await ReloadStatus(silent: true);
+
+ if (!string.IsNullOrWhiteSpace(unconfirmedEmailPattern))
+ {
+ Snackbar.Add("检测到账号存在待确认邮箱:你可以直接输入邮箱验证码完成确认。", Severity.Info);
+ }
+
return;
}
- unconfirmedEmailPattern = pattern;
- step = Step.Confirm;
+ unconfirmedEmailPattern = pattern ?? unconfirmedEmailPattern;
code = "";
Snackbar.Add("验证码已发送,请到邮箱中查看并输入验证码确认。", Severity.Success);
}
@@ -320,9 +327,4 @@
await Task.CompletedTask;
}
- private enum Step
- {
- SetEmail,
- Confirm
- }
}
diff --git a/src/TelegramPanel.Web/Components/Pages/Accounts.razor b/src/TelegramPanel.Web/Components/Pages/Accounts.razor
index 3100161..c6a3ae7 100644
--- a/src/TelegramPanel.Web/Components/Pages/Accounts.razor
+++ b/src/TelegramPanel.Web/Components/Pages/Accounts.razor
@@ -27,6 +27,14 @@
@c.Name
}
+
telegramStatus = new();
private HashSet selectedAccounts = new();
private readonly CancellationTokenSource _pageCts = new();
@@ -222,6 +231,7 @@
var (items, total) = await AccountManagement.QueryAccountsPagedAsync(
categoryId: categoryId,
search: searchString,
+ onlyWaste: onlyWaste,
pageIndex: state.Page,
pageSize: state.PageSize,
cancellationToken: cancellationToken);
@@ -1097,6 +1107,7 @@
var (_, total) = await AccountManagement.QueryAccountsPagedAsync(
categoryId: null,
search: null,
+ onlyWaste: false,
pageIndex: 0,
pageSize: 1,
cancellationToken: _pageCts.Token);