From 4cc3f9238964090118b7b45ed490d2b00a7f319b Mon Sep 17 00:00:00 2001 From: meoacgx Date: Fri, 2 Jan 2026 22:30:32 +0800 Subject: [PATCH] =?UTF-8?q?fix(bot-channels):=20=E8=87=AA=E5=8A=A8?= =?UTF-8?q?=E9=80=89=E6=8B=A9=E6=89=A7=E8=A1=8C=E8=B4=A6=E5=8F=B7=E6=8C=89?= =?UTF-8?q?=E6=9D=83=E9=99=90=E8=BF=87=E6=BB=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 解析 Bot API 管理员权限(can_invite_users/can_promote_members) - 批量邀请/设管理员自动选择时跳过无权限账号并给出原因 --- .../Services/Telegram/BotTelegramService.cs | 16 ++++++- .../Dialogs/BotChannelBatchAdminsDialog.razor | 43 +++++++++++-------- .../Dialogs/BotChannelBatchInviteDialog.razor | 43 +++++++++++-------- 3 files changed, 66 insertions(+), 36 deletions(-) diff --git a/src/TelegramPanel.Core/Services/Telegram/BotTelegramService.cs b/src/TelegramPanel.Core/Services/Telegram/BotTelegramService.cs index ea5eeb7..8b96e46 100644 --- a/src/TelegramPanel.Core/Services/Telegram/BotTelegramService.cs +++ b/src/TelegramPanel.Core/Services/Telegram/BotTelegramService.cs @@ -393,6 +393,8 @@ public class BotTelegramService var status = el.TryGetProperty("status", out var statusEl) ? (statusEl.GetString() ?? "") : ""; var customTitle = el.TryGetProperty("custom_title", out var ctEl) ? ctEl.GetString() : null; + var canInviteUsers = ReadBool(el, "can_invite_users"); + var canPromoteMembers = ReadBool(el, "can_promote_members"); if (!el.TryGetProperty("user", out var userEl) || userEl.ValueKind != JsonValueKind.Object) continue; @@ -410,7 +412,9 @@ public class BotTelegramService FirstName: firstName, LastName: lastName, Status: status, - CustomTitle: string.IsNullOrWhiteSpace(customTitle) ? null : customTitle.Trim() + CustomTitle: string.IsNullOrWhiteSpace(customTitle) ? null : customTitle.Trim(), + CanInviteUsers: canInviteUsers, + CanPromoteMembers: canPromoteMembers )); } @@ -646,7 +650,15 @@ public class BotTelegramService public sealed record BotChatInfo(long TelegramId, string Type, string? Title, string? Username, string? Description, int? MemberCount); - public sealed record BotChatAdminInfo(long UserId, string? Username, string? FirstName, string? LastName, string Status, string? CustomTitle) + public sealed record BotChatAdminInfo( + long UserId, + string? Username, + string? FirstName, + string? LastName, + string Status, + string? CustomTitle, + bool CanInviteUsers, + bool CanPromoteMembers) { public bool IsCreator => string.Equals(Status, "creator", StringComparison.OrdinalIgnoreCase); public string DisplayName diff --git a/src/TelegramPanel.Web/Components/Dialogs/BotChannelBatchAdminsDialog.razor b/src/TelegramPanel.Web/Components/Dialogs/BotChannelBatchAdminsDialog.razor index 7eda21c..e996f33 100644 --- a/src/TelegramPanel.Web/Components/Dialogs/BotChannelBatchAdminsDialog.razor +++ b/src/TelegramPanel.Web/Components/Dialogs/BotChannelBatchAdminsDialog.razor @@ -148,7 +148,7 @@ private readonly Random _rand = new(); private readonly List eligibleAccounts = new(); private readonly Dictionary accountsByUserId = new(); - private readonly Dictionary> channelAdminUserIds = new(); + private readonly Dictionary> channelAdmins = new(); private List channels = new(); private int selectedAccountId; @@ -228,7 +228,7 @@ loadingAdmins = true; adminSummary = null; eligibleAccounts.Clear(); - channelAdminUserIds.Clear(); + channelAdmins.Clear(); try { var eligibleUserIds = new HashSet(); @@ -236,13 +236,12 @@ foreach (var ch in channels) { - var adminIds = new HashSet(); + var admins = new List(); try { - var admins = await BotTelegram.GetChatAdminsAsync(BotId, ch.TelegramId, CancellationToken.None); + admins = await BotTelegram.GetChatAdminsAsync(BotId, ch.TelegramId, CancellationToken.None); foreach (var a in admins) { - adminIds.Add(a.UserId); if (accountsByUserId.ContainsKey(a.UserId)) eligibleUserIds.Add(a.UserId); } @@ -255,7 +254,7 @@ // ignore } - channelAdminUserIds[ch.TelegramId] = adminIds; + channelAdmins[ch.TelegramId] = admins; } foreach (var uid in eligibleUserIds) @@ -422,7 +421,7 @@ foreach (var ch in channels) { - var executorId = ResolveExecutorAccountId(ch); + var (executorId, reason) = ResolveExecutorAccountId(ch); if (executorId == null) { foreach (var _ in usernames) @@ -431,7 +430,7 @@ failed++; } - failures.Add($"{ch.Title}:无可用执行账号(需要该频道管理员且存在于系统账号)"); + failures.Add($"{ch.Title}:{reason ?? "无可用执行账号"}"); StateHasChanged(); continue; } @@ -530,27 +529,37 @@ } } - private int? ResolveExecutorAccountId(BotChannel channel) + private (int? ExecutorId, string? Reason) ResolveExecutorAccountId(BotChannel channel) { - if (!channelAdminUserIds.TryGetValue(channel.TelegramId, out var adminIds) || adminIds.Count == 0) - return null; + if (!channelAdmins.TryGetValue(channel.TelegramId, out var admins) || admins.Count == 0) + return (null, "无法获取频道管理员列表(请确认 Bot 已加入且为管理员)"); if (selectedAccountId > 0) { var selected = eligibleAccounts.FirstOrDefault(x => x.Id == selectedAccountId); if (selected == null || selected.UserId <= 0) - return null; + return (null, "所选执行账号无效"); - return adminIds.Contains(selected.UserId) ? selected.Id : null; + var admin = admins.FirstOrDefault(x => x.UserId == selected.UserId); + if (admin == null) + return (null, "所选执行账号不是该频道管理员"); + + if (!admin.IsCreator && !admin.CanPromoteMembers) + return (null, "所选执行账号缺少“添加管理员”权限"); + + return (selected.Id, null); } - foreach (var uid in adminIds) + foreach (var a in admins) { - if (accountsByUserId.TryGetValue(uid, out var acc)) - return acc.Id; + if (!a.IsCreator && !a.CanPromoteMembers) + continue; + + if (accountsByUserId.TryGetValue(a.UserId, out var acc)) + return (acc.Id, null); } - return null; + return (null, "无可用执行账号(需要该频道管理员且拥有“添加管理员”权限,并且在系统中存在)"); } private void CancelOrClose() diff --git a/src/TelegramPanel.Web/Components/Dialogs/BotChannelBatchInviteDialog.razor b/src/TelegramPanel.Web/Components/Dialogs/BotChannelBatchInviteDialog.razor index e49c2c6..486b3e4 100644 --- a/src/TelegramPanel.Web/Components/Dialogs/BotChannelBatchInviteDialog.razor +++ b/src/TelegramPanel.Web/Components/Dialogs/BotChannelBatchInviteDialog.razor @@ -116,7 +116,7 @@ private readonly Random _rand = new(); private readonly List eligibleAccounts = new(); private readonly Dictionary accountsByUserId = new(); - private readonly Dictionary> channelAdminUserIds = new(); + private readonly Dictionary> channelAdmins = new(); private List channels = new(); private int selectedAccountId; @@ -179,7 +179,7 @@ loadingAdmins = true; adminSummary = null; eligibleAccounts.Clear(); - channelAdminUserIds.Clear(); + channelAdmins.Clear(); try { var eligibleUserIds = new HashSet(); @@ -187,13 +187,12 @@ foreach (var ch in channels) { - var adminIds = new HashSet(); + var admins = new List(); try { - var admins = await BotTelegram.GetChatAdminsAsync(BotId, ch.TelegramId, CancellationToken.None); + admins = await BotTelegram.GetChatAdminsAsync(BotId, ch.TelegramId, CancellationToken.None); foreach (var a in admins) { - adminIds.Add(a.UserId); if (accountsByUserId.ContainsKey(a.UserId)) eligibleUserIds.Add(a.UserId); } @@ -206,7 +205,7 @@ // ignore: 无权限/风控/频道已失效等,后续按“无可用执行账号”处理 } - channelAdminUserIds[ch.TelegramId] = adminIds; + channelAdmins[ch.TelegramId] = admins; } foreach (var uid in eligibleUserIds) @@ -353,7 +352,7 @@ foreach (var ch in channels) { - var executorId = ResolveExecutorAccountId(ch); + var (executorId, reason) = ResolveExecutorAccountId(ch); if (executorId == null) { foreach (var _ in usernames) @@ -362,7 +361,7 @@ failed++; } - failures.Add($"{ch.Title}:无可用执行账号(需要该频道管理员且存在于系统账号)"); + failures.Add($"{ch.Title}:{reason ?? "无可用执行账号"}"); StateHasChanged(); continue; } @@ -461,27 +460,37 @@ } } - private int? ResolveExecutorAccountId(BotChannel channel) + private (int? ExecutorId, string? Reason) ResolveExecutorAccountId(BotChannel channel) { - if (!channelAdminUserIds.TryGetValue(channel.TelegramId, out var adminIds) || adminIds.Count == 0) - return null; + if (!channelAdmins.TryGetValue(channel.TelegramId, out var admins) || admins.Count == 0) + return (null, "无法获取频道管理员列表(请确认 Bot 已加入且为管理员)"); if (selectedAccountId > 0) { var selected = eligibleAccounts.FirstOrDefault(x => x.Id == selectedAccountId); if (selected == null || selected.UserId <= 0) - return null; + return (null, "所选执行账号无效"); - return adminIds.Contains(selected.UserId) ? selected.Id : null; + var admin = admins.FirstOrDefault(x => x.UserId == selected.UserId); + if (admin == null) + return (null, "所选执行账号不是该频道管理员"); + + if (!admin.IsCreator && !admin.CanInviteUsers) + return (null, "所选执行账号缺少“邀请用户”权限"); + + return (selected.Id, null); } - foreach (var uid in adminIds) + foreach (var a in admins) { - if (accountsByUserId.TryGetValue(uid, out var acc)) - return acc.Id; + if (!a.IsCreator && !a.CanInviteUsers) + continue; + + if (accountsByUserId.TryGetValue(a.UserId, out var acc)) + return (acc.Id, null); } - return null; + return (null, "无可用执行账号(需要该频道管理员且拥有“邀请用户”权限,并且在系统中存在)"); } private void CancelOrClose()