chore(release): v1.2.5

This commit is contained in:
meoacgx committed 2025-12-29 22:39:55 +08:00
1 parent 422a4396e3
commit b73f4141ae
7 files changed
+129 -56

No files matched your search

+4 -4
View File
@@ -5,10 +5,10 @@
<!-- 主版本: 重大变更/不兼容更新 -->
<!-- 次版本: 新功能/兼容性更新 -->
<!-- 修订号: Bug修复/小改进 -->
<Version>1.2.4</Version>
<AssemblyVersion>1.2.4.0</AssemblyVersion>
<FileVersion>1.2.4.0</FileVersion>
<InformationalVersion>1.2.4</InformationalVersion>
<Version>1.2.5</Version>
<AssemblyVersion>1.2.5.0</AssemblyVersion>
<FileVersion>1.2.5.0</FileVersion>
<InformationalVersion>1.2.5</InformationalVersion>
<!-- 项目元信息 -->
<Authors>Telegram Panel Contributors</Authors>
@@ -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<IReadOnlyList<Account>> 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;
@@ -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);
@@ -12,7 +12,7 @@ public class AccountRepository : Repository<Account>, IAccountRepository
{
}
private IQueryable<Account> BuildQuery(int? categoryId, string? search)
private IQueryable<Account> BuildQuery(int? categoryId, string? search, bool onlyWaste)
{
var query = _dbSet
.AsNoTracking()
@@ -48,6 +48,44 @@ public class AccountRepository : Repository<Account>, 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<Account>, 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<Account>, IAccountRepository
public async Task<IReadOnlyList<Account>> 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);
}
}
@@ -17,10 +17,12 @@ public interface IAccountRepository : IRepository<Account>
string? search,
int pageIndex,
int pageSize,
bool onlyWaste = false,
CancellationToken cancellationToken = default);
Task<IReadOnlyList<Account>> QueryAsync(
int? categoryId,
string? search,
bool onlyWaste = false,
CancellationToken cancellationToken = default);
}
@@ -43,18 +43,40 @@
该账号未开启两步验证,无法绑定找回邮箱。请先设置二级密码。
</MudAlert>
}
else if (step == Step.SetEmail)
else
{
<MudText Typo="Typo.subtitle2">发送验证码</MudText>
<MudTextField @bind-Value="currentPassword" Label="原二级密码" Variant="Variant.Outlined"
InputType="InputType.Password" Required="true" />
<MudTextField @bind-Value="email" Label="新找回邮箱" Variant="Variant.Outlined"
HelperText="将会向该邮箱发送验证码" Required="true" />
}
else
{
<MudStack direction="Row" Spacing="2" AlignItems="AlignItems.Center">
<MudButton Variant="Variant.Filled" Color="Color.Primary" Disabled="@running" OnClick="SendCode">
@if (running)
{
<MudProgressCircular Size="Size.Small" Indeterminate="true" Class="mr-2" />
<span>发送中...</span>
}
else
{
<span>发送验证码</span>
}
</MudButton>
<MudText Typo="Typo.caption" Color="Color.Secondary">
提示:即使“发送/重发”提示失败,但你实际收到了邮件验证码,也可以直接在下方输入验证码确认。
</MudText>
</MudStack>
<MudDivider Class="my-2" />
<MudText Typo="Typo.subtitle2">输入验证码确认</MudText>
<MudTextField @bind-Value="code" Label="邮箱验证码" Variant="Variant.Outlined"
HelperText="请到邮箱中查看验证码(注意垃圾箱/延迟)" Required="true" />
HelperText="请到邮箱中查看验证码(注意垃圾箱/延迟)。如提示过期,请先重发并使用最新验证码。"
Required="true" />
<MudStack direction="Row" Spacing="2">
<MudButton Variant="Variant.Outlined" Color="Color.Default" Disabled="@running" OnClick="Resend">
@@ -63,6 +85,17 @@
<MudButton Variant="Variant.Outlined" Color="Color.Default" Disabled="@running" OnClick="CancelVerification">
取消验证
</MudButton>
<MudButton Variant="Variant.Filled" Color="Color.Primary" Disabled="@running" OnClick="Confirm">
@if (running)
{
<MudProgressCircular Size="Size.Small" Indeterminate="true" Class="mr-2" />
<span>确认中...</span>
}
else
{
<span>确认绑定</span>
}
</MudButton>
</MudStack>
}
}
@@ -77,37 +110,7 @@
去设置二级密码
</MudButton>
}
else if (loaded && account != null && hasTwoFactorPassword)
{
@if (step == Step.SetEmail)
{
<MudButton Variant="Variant.Filled" Color="Color.Primary" Disabled="@running" OnClick="SendCode">
@if (running)
{
<MudProgressCircular Size="Size.Small" Indeterminate="true" Class="mr-2" />
<span>发送中...</span>
}
else
{
<span>发送验证码</span>
}
</MudButton>
}
else
{
<MudButton Variant="Variant.Filled" Color="Color.Primary" Disabled="@running" OnClick="Confirm">
@if (running)
{
<MudProgressCircular Size="Size.Small" Indeterminate="true" Class="mr-2" />
<span>确认中...</span>
}
else
{
<span>确认绑定</span>
}
</MudButton>
}
}
</DialogActions>
</MudDialog>
@@ -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
}
}
@@ -27,6 +27,14 @@
<MudSelectItem Value="@c.Id">@c.Name</MudSelectItem>
}
</MudSelect>
<MudCheckBox T="bool"
@bind-Value="onlyWaste"
@bind-Value:after="OnFiltersChanged"
Dense="true"
Disabled="@loading"
Label="只看废号"
Class="tp-input-full-xs"
Style="min-width: 140px; flex: 0 1 160px;" />
<MudTextField @bind-Value="searchString" Placeholder="搜索账号..." Adornment="Adornment.Start"
AdornmentIcon="@Icons.Material.Filled.Search" IconSize="Size.Medium" Class="mt-0 tp-input-full-xs"
Style="min-width: 200px; flex: 1; max-width: 400px;"
@@ -190,6 +198,7 @@
private bool loading = true;
private string searchString = "";
private int filterCategoryId = 0;
private bool onlyWaste;
private readonly Dictionary<int, TelegramAccountStatusResult> telegramStatus = new();
private HashSet<Account> 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);