@namespace TelegramPanel.Web.Components.Dialogs
@inject AccountManagementService AccountManagement
@inject TelegramPanel.Core.Services.AccountRiskService RiskService
@inject BatchTaskManagementService TaskManagement
@inject ISnackbar Snackbar
@inject IDialogService DialogService
@inject NavigationManager Navigation
@using System.Text.Json
@using TelegramPanel.Core.BatchTasks
@using TelegramPanel.Data.Entities
任务会提交到后台执行,可在【任务中心】查看进度与结果。
用户任务
Bot 任务
@foreach (var opt in AvailableOptions)
{
@opt.DisplayName
}
@if (CurrentOption != null && !string.IsNullOrWhiteSpace(CurrentOption.Description))
{
@CurrentOption.Description
}
@if (selectedTaskType == BatchTaskTypes.UserJoinSubscribe)
{
批量订阅频道/加群(用户任务):支持输入多个链接/用户名,换行分隔。支持:https://t.me/xxx、t.me/+hash、@username、username、tg://join?invite=hash。
选择执行账号:@selectedAccounts.Count / @accounts.Count
全选
清空
手机号
昵称
用户名
@context.Phone
@(string.IsNullOrWhiteSpace(context.Nickname) ? "-" : context.Nickname)
@(string.IsNullOrWhiteSpace(context.Username) ? "-" : "@" + context.Username)
暂无账号
}
else if (selectedTaskType == BatchTaskTypes.Invite)
{
当前版本暂未在任务中心内置“批量邀请”的创建表单,请使用“批量邀请”页面创建任务。
前往批量邀请
}
else if (selectedTaskType == BatchTaskTypes.SetAdmin)
{
当前版本暂未在任务中心内置“设置管理员”的创建表单,请使用“批量设置管理员”页面创建任务。
前往批量设置管理员
}
关闭
@if (creating)
{
提交中...
}
else
{
提交任务
}
@code
{
[CascadingParameter] private MudDialogInstance MudDialog { get; set; } = default!;
private sealed record TaskOption(string Category, string TaskType, string DisplayName, string? Description);
private static readonly List Options = new()
{
new("bot", BatchTaskTypes.Invite, "批量邀请", "对所选频道批量邀请用户名列表(Bot/执行账号需具备权限)。"),
new("bot", BatchTaskTypes.SetAdmin, "设置管理员", "对所选频道批量设置管理员(Bot/执行账号需具备权限)。"),
new("user", BatchTaskTypes.UserJoinSubscribe, "批量订阅/加群", "用户账号批量加入群组/订阅频道(Bot 无法主动加入)。")
};
private string _selectedCategory = "user";
private string selectedCategory
{
get => _selectedCategory;
set
{
if (string.Equals(_selectedCategory, value, StringComparison.Ordinal))
return;
_selectedCategory = value;
EnsureTaskTypeValid();
}
}
private string selectedTaskType = BatchTaskTypes.UserJoinSubscribe;
private bool creating;
// join/subscribe config
private string linksText = "";
private int delayMs = 2000;
private bool loadingAccounts;
private List accounts = new();
private string accountSearch = "";
private HashSet selectedAccounts = new();
private IEnumerable AvailableOptions => Options.Where(o => o.Category == selectedCategory);
private TaskOption? CurrentOption => Options.FirstOrDefault(o => o.TaskType == selectedTaskType);
private IEnumerable FilteredAccounts =>
accounts.Where(a =>
{
if (string.IsNullOrWhiteSpace(accountSearch))
return true;
var q = accountSearch.Trim();
return a.Phone.Contains(q, StringComparison.OrdinalIgnoreCase)
|| (a.Nickname?.Contains(q, StringComparison.OrdinalIgnoreCase) ?? false)
|| (a.Username?.Contains(q, StringComparison.OrdinalIgnoreCase) ?? false);
});
protected override async Task OnInitializedAsync()
{
EnsureTaskTypeValid();
await LoadAccounts();
}
private async Task LoadAccounts()
{
loadingAccounts = true;
try
{
var list = await AccountManagement.GetActiveAccountsAsync();
accounts = list.OrderBy(a => a.Id).ToList();
selectedAccounts = selectedAccounts.Where(a => accounts.Any(x => x.Id == a.Id)).ToHashSet();
}
catch (Exception ex)
{
Snackbar.Add($"加载账号失败:{ex.Message}", Severity.Error);
}
finally
{
loadingAccounts = false;
}
}
private void EnsureTaskTypeValid()
{
if (AvailableOptions.All(o => o.TaskType != selectedTaskType))
{
selectedTaskType = AvailableOptions.FirstOrDefault()?.TaskType
?? Options.First().TaskType;
}
}
private void SelectAll() => selectedAccounts = accounts.ToHashSet();
private void ClearSelection() => selectedAccounts = new HashSet();
private async Task Create()
{
if (creating)
return;
EnsureTaskTypeValid();
if (selectedTaskType != BatchTaskTypes.UserJoinSubscribe)
{
Snackbar.Add("当前任务类型暂不支持从任务中心直接创建", Severity.Info);
return;
}
var links = ParseLines(linksText);
if (links.Count == 0)
{
Snackbar.Add("请至少输入一个频道/群链接", Severity.Warning);
return;
}
var selected = selectedAccounts.ToList();
if (selected.Count == 0)
{
Snackbar.Add("请至少选择一个执行账号", Severity.Warning);
return;
}
// 批量风控检查:加入群/订阅频道是敏感操作
var batchCheck = RiskService.CheckBatchAccounts(selected);
if (batchCheck.HasRiskyAccounts)
{
var parameters = new DialogParameters
{
["Message"] = $"检测到 {batchCheck.RiskyCount} 个风险账号",
["DetailedMessage"] = batchCheck.GetRiskySummary(),
["ShowRecommendations"] = true,
["ShowExcludeOption"] = true,
["RiskyAccounts"] = batchCheck.RiskyAccounts.ToList()
};
var options = new DialogOptions { CloseOnEscapeKey = true, MaxWidth = MaxWidth.Large };
var dialog = DialogService.Show("批量操作风控警告", parameters, options);
var result = await dialog.Result;
if (result.Canceled)
return;
if (result.Data is TelegramPanel.Core.Services.RiskWarningAction action && action == TelegramPanel.Core.Services.RiskWarningAction.ExcludeRisky)
{
selected = batchCheck.SafeAccounts.ToList();
if (selected.Count == 0)
{
Snackbar.Add("排除风险账号后无剩余账号", Severity.Info);
return;
}
}
}
bool? confirm = await DialogService.ShowMessageBox(
"确认提交任务",
$"将对 {selected.Count} 个账号执行 {links.Count} 个链接的订阅/加群操作,总计 {selected.Count * links.Count} 次。是否继续?",
yesText: "继续", cancelText: "取消");
if (confirm != true)
return;
creating = true;
try
{
var ids = selected.Select(a => a.Id).Distinct().ToList();
var task = new BatchTask
{
TaskType = BatchTaskTypes.UserJoinSubscribe,
Total = ids.Count * links.Count,
Completed = 0,
Failed = 0,
Config = JsonSerializer.Serialize(new
{
AccountIds = ids,
Links = links,
DelayMs = delayMs
})
};
var created = await TaskManagement.CreateTaskAsync(task);
Snackbar.Add($"已提交后台任务 #{created.Id}(批量订阅/加群)。请到【任务中心】查看进度。", Severity.Success);
MudDialog.Close(DialogResult.Ok(created.Id));
}
catch (Exception ex)
{
Snackbar.Add($"提交失败:{ex.Message}", Severity.Error);
}
finally
{
creating = false;
}
}
private void NavigateTo(string url)
{
MudDialog.Close();
Navigation.NavigateTo(url);
}
private static List ParseLines(string? text)
{
if (string.IsNullOrWhiteSpace(text))
return new List();
return text
.Split(new[] { "\r\n", "\n", "\r" }, StringSplitOptions.RemoveEmptyEntries)
.Select(x => (x ?? string.Empty).Trim())
.Where(x => !string.IsNullOrWhiteSpace(x))
.Distinct(StringComparer.OrdinalIgnoreCase)
.ToList();
}
private void Cancel() => MudDialog.Close();
}