From d6edc8cf958f1c219ebfe3fb2418996a04a176dd Mon Sep 17 00:00:00 2001 From: meoacgx Date: Sat, 3 Jan 2026 13:35:18 +0800 Subject: [PATCH] =?UTF-8?q?fix(channel-admin):=20bot/=E9=A2=91=E9=81=93?= =?UTF-8?q?=E6=9D=83=E9=99=90=E8=87=AA=E5=8A=A8=E9=99=8D=E7=BA=A7=E9=81=BF?= =?UTF-8?q?=E5=85=8D403?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 频道非forum自动移除 ManageTopics;目标为Bot自动移除 InviteUsers 等不适用权限 - 执行账号可授予范围内自动降级重试,减少 RIGHT_FORBIDDEN - Bot频道批量设管理员仅要求执行账号具备添加管理员权限 --- .../Services/Telegram/ChannelService.cs | 136 ++++++++++++++++-- .../Dialogs/BotChannelBatchAdminsDialog.razor | 13 +- 2 files changed, 132 insertions(+), 17 deletions(-) diff --git a/src/TelegramPanel.Core/Services/Telegram/ChannelService.cs b/src/TelegramPanel.Core/Services/Telegram/ChannelService.cs index a4dfdf0..38dd8df 100644 --- a/src/TelegramPanel.Core/Services/Telegram/ChannelService.cs +++ b/src/TelegramPanel.Core/Services/Telegram/ChannelService.cs @@ -354,6 +354,9 @@ public class ChannelService : IChannelService if (ex.Message.Contains("USER_ALREADY_PARTICIPANT", StringComparison.OrdinalIgnoreCase)) return new InviteResult(username, true); + if (ex.Message.Contains("RIGHT_FORBIDDEN", StringComparison.OrdinalIgnoreCase)) + return new InviteResult(username, false, "RIGHT_FORBIDDEN:执行账号缺少邀请权限,或该频道限制邀请"); + return new InviteResult(username, false, ex.Message); } catch (Exception ex) @@ -394,15 +397,72 @@ public class ChannelService : IChannelService var resolved = await client.Contacts_ResolveUsername(username); - var chatAdminRights = ConvertAdminRights(rights); + var requestedRights = SanitizeRequestedAdminRights(channel, resolved.User, rights); + var effectiveRights = requestedRights; + if (requestedRights != rights) + { + _logger.LogInformation( + "Admin rights sanitized for channel {ChannelId} target @{Username}: requested={Requested} sanitized={Sanitized}", + channelId, username, rights, requestedRights); + } + + async Task PromoteAsync(Interfaces.AdminRights toGrant) + { + var chatAdminRights = ConvertAdminRights(toGrant); + await client.Channels_EditAdmin(channel, resolved.User, chatAdminRights, title); + } + + async Task TryGetExecutorAdminRightsAsync() + { + try + { + var meId = client.User?.id ?? 0; + if (meId <= 0) + return null; + return await TryGetGrantedAdminRightsAsync(client, channel, meId); + } + catch + { + return null; + } + } + + async Task TryPromoteWithFallbackAsync(Interfaces.AdminRights toGrant) + { + try + { + await PromoteAsync(toGrant); + effectiveRights = toGrant; + return true; + } + catch (RpcException ex) when (ex.Message.Contains("RIGHT_FORBIDDEN", StringComparison.OrdinalIgnoreCase)) + { + // 如果执行账号能“提升管理员”,但不能授予某些权限(比如 add_admins/anonymous/manage_topics),则交集降级后重试一次 + var executorRights = await TryGetExecutorAdminRightsAsync(); + if (!executorRights.HasValue) + throw; + + var reduced = toGrant & executorRights.Value; + if (reduced == toGrant || reduced == Interfaces.AdminRights.None) + throw; + + _logger.LogInformation( + "Promote rights downgraded for channel {ChannelId}: requested={Requested} executor={Executor} effective={Effective}", + channelId, requestedRights, executorRights.Value, reduced); + + await PromoteAsync(reduced); + effectiveRights = reduced; + return true; + } + } try { - await client.Channels_EditAdmin(channel, resolved.User, chatAdminRights, title); + await TryPromoteWithFallbackAsync(requestedRights); } catch (RpcException ex) when (ex.Message.Contains("USER_NOT_PARTICIPANT", StringComparison.OrdinalIgnoreCase)) { - // 目标用户不在频道:先尝试邀请进频道,再重试一次提升权限 + // 目标用户不在频道:先尝试邀请进频道,再提升权限(邀请可能会因缺少 invite 权限而失败) try { await client.Channels_InviteToChannel(channel, new InputUser(resolved.User.id, resolved.User.access_hash)); @@ -413,16 +473,33 @@ public class ChannelService : IChannelService } catch (RpcException inviteEx) when (inviteEx.Message.Contains("USER_BOT", StringComparison.OrdinalIgnoreCase)) { - // 目标是 Bot:邀请成员可能不被允许,继续尝试提升(部分频道/权限组合仍可直接提升) + // 目标是 Bot:邀请成员可能不被允许,继续尝试提升 _logger.LogDebug(inviteEx, "Invite bot as member is not allowed for channel {ChannelId}, continue promoting", channelId); } + catch (RpcException inviteEx) when (inviteEx.Message.Contains("RIGHT_FORBIDDEN", StringComparison.OrdinalIgnoreCase)) + { + throw new InvalidOperationException( + "目标用户不在频道,且执行账号缺少“邀请用户”权限,无法先邀请再设置管理员。", inviteEx); + } - await client.Channels_EditAdmin(channel, resolved.User, chatAdminRights, title); + try + { + await TryPromoteWithFallbackAsync(requestedRights); + } + catch (RpcException ex2) when (ex2.Message.Contains("RIGHT_FORBIDDEN", StringComparison.OrdinalIgnoreCase)) + { + var executor = await TryGetExecutorAdminRightsAsync(); + var executorHint = executor.HasValue ? $"执行账号可授予:{FormatAdminRights(executor.Value)}。" : ""; + throw new InvalidOperationException( + $"RIGHT_FORBIDDEN:执行账号无权设置管理员,或请求授予的权限超出可授予范围。{executorHint}", ex2); + } } catch (RpcException ex) when (ex.Message.Contains("RIGHT_FORBIDDEN", StringComparison.OrdinalIgnoreCase)) { + var executor = await TryGetExecutorAdminRightsAsync(); + var executorHint = executor.HasValue ? $"执行账号可授予:{FormatAdminRights(executor.Value)}。" : ""; throw new InvalidOperationException( - "RIGHT_FORBIDDEN:执行账号缺少权限。请确认执行账号是频道创建者,或在频道里拥有“邀请用户/添加管理员”等权限。"); + $"RIGHT_FORBIDDEN:执行账号无权设置管理员,或请求授予的权限超出可授予范围。{executorHint}", ex); } // 权限校验:Telegram 可能会静默“削减”你请求的权限(典型:执行账号本身没有 add_admins/promote 权限) @@ -431,12 +508,12 @@ public class ChannelService : IChannelService var granted = await TryGetGrantedAdminRightsAsync(client, channel, resolved.User.id); if (granted.HasValue) { - var missing = rights & ~granted.Value; + var missing = effectiveRights & ~granted.Value; if (missing != Interfaces.AdminRights.None) { throw new InvalidOperationException( $"已设置管理员,但部分权限未生效:{FormatAdminRights(missing)}。" + - "请确认:执行账号是该频道创建者,或执行账号在频道内拥有“添加管理员”权限。"); + "请确认:执行账号是该频道创建者,或执行账号在频道内拥有足够的授权权限。"); } } } @@ -454,6 +531,49 @@ public class ChannelService : IChannelService return true; } + private static Interfaces.AdminRights SanitizeRequestedAdminRights(Channel channel, User targetUser, Interfaces.AdminRights rights) + { + var sanitized = rights; + + // 频道(broadcast)不支持“管理话题”;仅 forum 超级群才有话题 + var isMegagroup = TryGetBoolMember(channel, "megagroup"); + var isForum = TryGetBoolMember(channel, "forum"); + if (!isMegagroup || !isForum) + sanitized &= ~Interfaces.AdminRights.ManageTopics; + + // Bot 在频道内的可授予权限有额外限制(例如 invite_users 常见会被拒绝) + var isBot = TryGetBoolMember(targetUser, "bot"); + if (isBot) + { + sanitized &= ~Interfaces.AdminRights.InviteUsers; + sanitized &= ~Interfaces.AdminRights.Anonymous; + sanitized &= ~Interfaces.AdminRights.ManageTopics; + } + + return sanitized; + } + + private static bool TryGetBoolMember(object obj, string memberName) + { + try + { + var t = obj.GetType(); + var prop = t.GetProperty(memberName); + if (prop != null && prop.PropertyType == typeof(bool)) + return (bool)(prop.GetValue(obj) ?? false); + + var field = t.GetField(memberName); + if (field != null && field.FieldType == typeof(bool)) + return (bool)(field.GetValue(obj) ?? false); + } + catch + { + // ignore + } + + return false; + } + public async Task> BatchSetAdminsAsync(int accountId, long channelId, List requests) { var results = new List(); diff --git a/src/TelegramPanel.Web/Components/Dialogs/BotChannelBatchAdminsDialog.razor b/src/TelegramPanel.Web/Components/Dialogs/BotChannelBatchAdminsDialog.razor index d00ff22..bd6ec56 100644 --- a/src/TelegramPanel.Web/Components/Dialogs/BotChannelBatchAdminsDialog.razor +++ b/src/TelegramPanel.Web/Components/Dialogs/BotChannelBatchAdminsDialog.razor @@ -544,13 +544,8 @@ if (admin == null) return (null, "所选执行账号不是该频道管理员"); - if (!admin.IsCreator) - { - if (!admin.CanPromoteMembers) - return (null, "所选执行账号缺少“添加管理员”权限"); - if (!admin.CanInviteUsers) - return (null, "所选执行账号缺少“邀请用户”权限(添加新管理员可能需要先邀请进频道)"); - } + if (!admin.IsCreator && !admin.CanPromoteMembers) + return (null, "所选执行账号缺少“添加管理员”权限"); return (selected.Id, null); } @@ -561,14 +556,14 @@ foreach (var a in admins) { - if (!a.IsCreator && (!a.CanPromoteMembers || !a.CanInviteUsers)) + 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()