fix: harden channel rights and sticker compatibility

Co-authored-by: HSgram <3013954224@qq.com>
This commit is contained in:
A 2026-07-01 22:28:24 +08:00
parent 6867d201ed
commit 599453a3c4
33 changed files with 1520 additions and 130 deletions

View file

@ -240,20 +240,29 @@ func NormalizeFullMegagroupAdminRights(ch Channel, rights ChannelAdminRights) Ch
// ChannelBannedRights is a domain-only representation of Telegram banned rights.
type ChannelBannedRights struct {
ViewMessages bool
SendMessages bool
SendMedia bool
SendStickers bool
SendGifs bool
SendGames bool
SendInline bool
EmbedLinks bool
SendPolls bool
ChangeInfo bool
InviteUsers bool
PinMessages bool
EditRank bool
UntilDate int
ViewMessages bool
SendMessages bool
SendMedia bool
SendStickers bool
SendGifs bool
SendGames bool
SendInline bool
EmbedLinks bool
SendPolls bool
ChangeInfo bool
InviteUsers bool
PinMessages bool
ManageTopics bool
SendPhotos bool
SendVideos bool
SendRoundvideos bool
SendAudios bool
SendVoices bool
SendDocs bool
SendPlain bool
EditRank bool
SendReactions bool
UntilDate int
}
// ChannelReactionPolicyType describes which reactions are allowed in a channel.

View file

@ -0,0 +1,109 @@
package domain
import "strings"
// ChannelBannedRightsBlockMessage reports whether a non-admin channel member is
// blocked by fine-grained chatBannedRights for this concrete message payload.
func ChannelBannedRightsBlockMessage(req SendChannelMessageRequest, channel Channel, member ChannelMember, selfBoostsApplied int) bool {
if req.Action != nil || channelBannedRightsBypassed(channel, member) {
return false
}
if req.Media.IsZero() {
if strings.TrimSpace(req.Message) == "" {
return false
}
return channelBannedRightsBlockWithBoost(channel, member.BannedRights.SendPlain, channel.DefaultBannedRights.SendPlain, selfBoostsApplied)
}
return ChannelBannedRightsBlockMedia(channel, member, req.Media, selfBoostsApplied)
}
// ChannelBannedRightsBlockMedia applies both legacy send_media and modern
// per-media banned rights to one message media payload.
func ChannelBannedRightsBlockMedia(channel Channel, member ChannelMember, media *MessageMedia, selfBoostsApplied int) bool {
if media.IsZero() || channelBannedRightsBypassed(channel, member) {
return false
}
return channelBannedRightsBlockWithBoost(
channel,
channelBannedRightsBlockMediaKind(member.BannedRights, media),
channelBannedRightsBlockMediaKind(channel.DefaultBannedRights, media),
selfBoostsApplied,
)
}
// ChannelBannedRightsBlockReactions applies send_reactions. Empty reaction
// vectors are handled by callers as removals and should remain allowed.
func ChannelBannedRightsBlockReactions(channel Channel, member ChannelMember, selfBoostsApplied int) bool {
if channelBannedRightsBypassed(channel, member) {
return false
}
return channelBannedRightsBlockWithBoost(channel, member.BannedRights.SendReactions, channel.DefaultBannedRights.SendReactions, selfBoostsApplied)
}
// ChannelBannedRightsBlockManageTopics applies manage_topics to topic create,
// edit, and closed-topic reply bypasses.
func ChannelBannedRightsBlockManageTopics(channel Channel, member ChannelMember, selfBoostsApplied int) bool {
if channelBannedRightsBypassed(channel, member) {
return false
}
return channelBannedRightsBlockWithBoost(channel, member.BannedRights.ManageTopics, channel.DefaultBannedRights.ManageTopics, selfBoostsApplied)
}
func channelBannedRightsBypassed(channel Channel, member ChannelMember) bool {
return channel.Broadcast || member.Role == ChannelRoleCreator || member.Role == ChannelRoleAdmin
}
func channelBannedRightsBlockWithBoost(channel Channel, memberBlocked, defaultBlocked bool, selfBoostsApplied int) bool {
if memberBlocked {
return true
}
if !defaultBlocked {
return false
}
return channel.BoostsUnrestrict == 0 || selfBoostsApplied < channel.BoostsUnrestrict
}
func channelBannedRightsBlockMediaKind(rights ChannelBannedRights, media *MessageMedia) bool {
if rights.SendMedia {
return true
}
switch media.Kind {
case MessageMediaKindPhoto:
return rights.SendPhotos
case MessageMediaKindDocument:
return channelBannedRightsBlockDocument(rights, media)
case MessageMediaKindPoll:
return rights.SendPolls
case MessageMediaKindDice:
return rights.SendGames
case MessageMediaKindWebPage:
return rights.EmbedLinks
default:
return rights.SendMedia
}
}
func channelBannedRightsBlockDocument(rights ChannelBannedRights, media *MessageMedia) bool {
if media == nil || media.Document == nil {
return rights.SendDocs
}
if media.Document.IsSticker() {
return rights.SendStickers
}
if media.Document.IsGif() {
return rights.SendGifs
}
if media.Round {
return rights.SendRoundvideos
}
if media.Voice {
return rights.SendVoices
}
if media.Video {
return rights.SendVideos
}
if media.IsMusic() {
return rights.SendAudios
}
return rights.SendDocs
}

View file

@ -653,10 +653,16 @@ type StickerKeyword struct {
Keywords []string `json:"keywords"`
}
// StickerSetSystemKeyEmojiDefaultStatuses 是 inputStickerSetEmojiDefaultStatuses
// 对应的系统集标识:premium 用户 emoji status 选择器的"默认状态"主体集
// (messages.getStickerSet 与 account.getDefaultEmojiStatuses 共用)。
const StickerSetSystemKeyEmojiDefaultStatuses = "emoji_default_statuses"
const (
// StickerSetSystemKeyEmojiDefaultStatuses 是 inputStickerSetEmojiDefaultStatuses
// 对应的系统集标识:premium 用户 emoji status 选择器的"默认状态"主体集
// (messages.getStickerSet 与 account.getDefaultEmojiStatuses 共用)。
StickerSetSystemKeyEmojiDefaultStatuses = "emoji_default_statuses"
// StickerSetSystemKeyEmojiDefaultTopicIcons 对应论坛 topic 默认图标系统集。
StickerSetSystemKeyEmojiDefaultTopicIcons = "emoji_default_topic_icons"
// StickerSetSystemKeyPremiumGifts 对应 premium gifts 系统集。
StickerSetSystemKeyPremiumGifts = "premium_gifts"
)
// StickerSetKind 区分贴纸集用途(影响 getAllStickers / getEmojiStickers 归类)。
type StickerSetKind string