304 lines
11 KiB
Plaintext
304 lines
11 KiB
Plaintext
@page "/accounts/import"
|
||
@inject AccountImportService AccountImportService
|
||
@inject IConfiguration Configuration
|
||
@inject ISnackbar Snackbar
|
||
|
||
<PageTitle>导入账号 - Telegram Panel</PageTitle>
|
||
|
||
<MudText Typo="Typo.h4" Class="mb-4">导入账号</MudText>
|
||
|
||
<MudGrid>
|
||
<MudItem xs="12">
|
||
<MudCard Class="mb-4">
|
||
<MudCardHeader>
|
||
<MudText Typo="Typo.h6">压缩包导入(推荐)</MudText>
|
||
</MudCardHeader>
|
||
<MudCardContent>
|
||
<MudAlert Severity="Severity.Info" Variant="Variant.Outlined" Dense="true">
|
||
支持批量导入账号压缩包:每个账号一个独立子文件夹,文件夹内包含一个 <strong>.json</strong> + 一个 <strong>.session</strong>。
|
||
</MudAlert>
|
||
|
||
<MudFileUpload T="IReadOnlyList<IBrowserFile>" FilesChanged="OnZipFilesChanged" Accept=".zip" MaximumFileCount="1">
|
||
<ActivatorContent>
|
||
<MudButton Variant="Variant.Filled" Color="Color.Primary" StartIcon="@Icons.Material.Filled.FolderZip" Class="mt-3">
|
||
选择 Zip 压缩包
|
||
</MudButton>
|
||
</ActivatorContent>
|
||
</MudFileUpload>
|
||
|
||
@if (zipFile != null)
|
||
{
|
||
<MudText Typo="Typo.body2" Class="mt-3">已选择:@zipFile.Name(@zipFile.Size bytes)</MudText>
|
||
}
|
||
|
||
<MudButton Variant="Variant.Filled" Color="Color.Success" FullWidth="true"
|
||
Disabled="@(zipFile == null || importingZip)" Class="mt-4"
|
||
OnClick="@(async (MouseEventArgs _) => await ImportZip())">
|
||
@if (importingZip)
|
||
{
|
||
<MudProgressCircular Size="Size.Small" Indeterminate="true" Class="mr-2" />
|
||
<span>导入中...</span>
|
||
}
|
||
else
|
||
{
|
||
<span>开始导入 Zip</span>
|
||
}
|
||
</MudButton>
|
||
</MudCardContent>
|
||
</MudCard>
|
||
</MudItem>
|
||
|
||
<MudItem xs="12" md="6">
|
||
<MudCard>
|
||
<MudCardHeader>
|
||
<MudText Typo="Typo.h6">Session 文件导入</MudText>
|
||
</MudCardHeader>
|
||
<MudCardContent>
|
||
<MudFileUpload T="IReadOnlyList<IBrowserFile>" FilesChanged="OnFilesChanged" Accept=".session" MaximumFileCount="100">
|
||
<ActivatorContent>
|
||
<MudButton Variant="Variant.Filled" Color="Color.Primary" StartIcon="@Icons.Material.Filled.CloudUpload" Class="mt-4">
|
||
选择 Session 文件
|
||
</MudButton>
|
||
</ActivatorContent>
|
||
</MudFileUpload>
|
||
|
||
@if (selectedFiles.Any())
|
||
{
|
||
<MudText Typo="Typo.body2" Class="mt-3">已选择 @selectedFiles.Count 个文件</MudText>
|
||
<MudList T="string" Dense="true">
|
||
@foreach (var file in selectedFiles)
|
||
{
|
||
<MudListItem T="string" Icon="@Icons.Material.Filled.Description">@file.Name</MudListItem>
|
||
}
|
||
</MudList>
|
||
}
|
||
|
||
<MudButton Variant="Variant.Filled" Color="Color.Success" FullWidth="true"
|
||
Disabled="@(!selectedFiles.Any() || importing)" Class="mt-4"
|
||
OnClick="@(async (MouseEventArgs _) => await ImportSessionFiles())">
|
||
@if (importing)
|
||
{
|
||
<MudProgressCircular Size="Size.Small" Indeterminate="true" Class="mr-2" />
|
||
<span>导入中...</span>
|
||
}
|
||
else
|
||
{
|
||
<span>开始导入</span>
|
||
}
|
||
</MudButton>
|
||
</MudCardContent>
|
||
</MudCard>
|
||
</MudItem>
|
||
|
||
<MudItem xs="12" md="6">
|
||
<MudCard>
|
||
<MudCardHeader>
|
||
<MudText Typo="Typo.h6">StringSession 导入</MudText>
|
||
</MudCardHeader>
|
||
<MudCardContent>
|
||
<MudTextField @bind-Value="sessionString" Label="Session String (Base64)" Variant="Variant.Outlined"
|
||
Lines="5" Required="true" Class="mt-3" />
|
||
|
||
<MudButton Variant="Variant.Filled" Color="Color.Success" FullWidth="true"
|
||
Disabled="@(string.IsNullOrEmpty(sessionString) || importingString)" Class="mt-4"
|
||
OnClick="@(async (MouseEventArgs _) => await ImportStringSession())">
|
||
@if (importingString)
|
||
{
|
||
<MudProgressCircular Size="Size.Small" Indeterminate="true" Class="mr-2" />
|
||
<span>导入中...</span>
|
||
}
|
||
else
|
||
{
|
||
<span>导入 StringSession</span>
|
||
}
|
||
</MudButton>
|
||
</MudCardContent>
|
||
</MudCard>
|
||
</MudItem>
|
||
</MudGrid>
|
||
|
||
@if (importResults.Any())
|
||
{
|
||
<MudCard Class="mt-4">
|
||
<MudCardHeader>
|
||
<MudText Typo="Typo.h6">导入结果</MudText>
|
||
</MudCardHeader>
|
||
<MudCardContent>
|
||
<MudTable Items="@importResults" Hover="true">
|
||
<HeaderContent>
|
||
<MudTh>手机号</MudTh>
|
||
<MudTh>用户ID</MudTh>
|
||
<MudTh>用户名</MudTh>
|
||
<MudTh>状态</MudTh>
|
||
<MudTh>消息</MudTh>
|
||
</HeaderContent>
|
||
<RowTemplate>
|
||
<MudTd DataLabel="手机号">@(context.Phone ?? "-")</MudTd>
|
||
<MudTd DataLabel="用户ID">@(context.UserId?.ToString() ?? "-")</MudTd>
|
||
<MudTd DataLabel="用户名">@(context.Username ?? "-")</MudTd>
|
||
<MudTd DataLabel="状态">
|
||
<MudChip T="string" Size="Size.Small" Color="@(context.Success ? Color.Success : Color.Error)">
|
||
@(context.Success ? "成功" : "失败")
|
||
</MudChip>
|
||
</MudTd>
|
||
<MudTd DataLabel="消息">@(context.Success ? "成功" : (context.Error ?? "失败"))</MudTd>
|
||
</RowTemplate>
|
||
</MudTable>
|
||
</MudCardContent>
|
||
</MudCard>
|
||
}
|
||
|
||
@code {
|
||
private List<IBrowserFile> selectedFiles = new();
|
||
private bool importing = false;
|
||
|
||
private IBrowserFile? zipFile;
|
||
private bool importingZip = false;
|
||
|
||
private string sessionString = "";
|
||
private bool importingString = false;
|
||
|
||
private List<ImportResult> importResults = new();
|
||
|
||
private bool TryGetTelegramApi(out int apiId, out string apiHash)
|
||
{
|
||
apiHash = Configuration["Telegram:ApiHash"] ?? "";
|
||
return int.TryParse(Configuration["Telegram:ApiId"], out apiId)
|
||
&& apiId > 0
|
||
&& !string.IsNullOrWhiteSpace(apiHash);
|
||
}
|
||
|
||
private void OnFilesChanged(IReadOnlyList<IBrowserFile> files)
|
||
{
|
||
selectedFiles = files.ToList();
|
||
}
|
||
|
||
private void OnZipFilesChanged(IReadOnlyList<IBrowserFile> files)
|
||
{
|
||
zipFile = files.FirstOrDefault();
|
||
}
|
||
|
||
private async Task ImportZip()
|
||
{
|
||
if (zipFile == null)
|
||
{
|
||
Snackbar.Add("请先选择 Zip 压缩包", Severity.Error);
|
||
return;
|
||
}
|
||
|
||
importingZip = true;
|
||
importResults.Clear();
|
||
|
||
try
|
||
{
|
||
var results = await AccountImportService.ImportFromZipAsync(zipFile);
|
||
importResults.AddRange(results);
|
||
|
||
var successCount = results.Count(r => r.Success);
|
||
var failureCount = results.Count - successCount;
|
||
|
||
if (successCount > 0)
|
||
Snackbar.Add($"成功导入 {successCount} 个账号", Severity.Success);
|
||
if (failureCount > 0)
|
||
Snackbar.Add($"{failureCount} 个账号导入失败", Severity.Warning);
|
||
|
||
zipFile = null;
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Snackbar.Add($"导入失败:{ex.Message}", Severity.Error);
|
||
}
|
||
finally
|
||
{
|
||
importingZip = false;
|
||
}
|
||
}
|
||
|
||
private async Task ImportSessionFiles()
|
||
{
|
||
if (!TryGetTelegramApi(out var apiId, out var apiHash))
|
||
{
|
||
Snackbar.Add("请先在【系统设置】中配置全局 Telegram API(ApiId/ApiHash)", Severity.Error);
|
||
return;
|
||
}
|
||
|
||
importing = true;
|
||
importResults.Clear();
|
||
|
||
try
|
||
{
|
||
var results = await AccountImportService.ImportFromBrowserFilesAsync(
|
||
selectedFiles,
|
||
apiId,
|
||
apiHash
|
||
);
|
||
|
||
importResults.AddRange(results);
|
||
|
||
var successCount = results.Count(r => r.Success);
|
||
var failureCount = results.Count - successCount;
|
||
|
||
if (successCount > 0)
|
||
{
|
||
Snackbar.Add($"成功导入 {successCount} 个账号", Severity.Success);
|
||
}
|
||
|
||
if (failureCount > 0)
|
||
{
|
||
Snackbar.Add($"{failureCount} 个账号导入失败", Severity.Warning);
|
||
}
|
||
|
||
// 清空已选文件
|
||
selectedFiles.Clear();
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Snackbar.Add($"导入失败:{ex.Message}", Severity.Error);
|
||
}
|
||
finally
|
||
{
|
||
importing = false;
|
||
}
|
||
}
|
||
|
||
private async Task ImportStringSession()
|
||
{
|
||
if (!TryGetTelegramApi(out var apiId, out var apiHash))
|
||
{
|
||
Snackbar.Add("请先在【系统设置】中配置全局 Telegram API(ApiId/ApiHash)", Severity.Error);
|
||
return;
|
||
}
|
||
|
||
importingString = true;
|
||
|
||
try
|
||
{
|
||
var result = await AccountImportService.ImportFromStringSessionAsync(
|
||
sessionString,
|
||
apiId,
|
||
apiHash
|
||
);
|
||
|
||
importResults.Add(result);
|
||
|
||
if (result.Success)
|
||
{
|
||
Snackbar.Add($"导入成功:{result.Phone}", Severity.Success);
|
||
sessionString = "";
|
||
}
|
||
else
|
||
{
|
||
Snackbar.Add($"导入失败:{result.Error ?? "未知错误"}", Severity.Error);
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Snackbar.Add($"导入失败:{ex.Message}", Severity.Error);
|
||
}
|
||
finally
|
||
{
|
||
importingString = false;
|
||
}
|
||
}
|
||
}
|