fix: emit channel avatar service messages

This commit is contained in:
A 2026-07-05 15:14:41 +08:00
parent 4d9f1e271d
commit c38dd73fdd
14 changed files with 346 additions and 27 deletions

View file

@ -40,7 +40,7 @@ type ChannelStore interface {
ResolvePublicChannelUsername(ctx context.Context, viewerUserID int64, username string) (domain.Channel, bool, error)
SearchPublicChannels(ctx context.Context, viewerUserID int64, query string, limit int) (domain.PublicChannelSearchResult, error)
SetSignatures(ctx context.Context, userID, channelID int64, enabled bool) (domain.Channel, error)
SetChannelPhoto(ctx context.Context, userID, channelID int64, photo *domain.Photo) (domain.Channel, error)
SetChannelPhoto(ctx context.Context, userID, channelID int64, photo *domain.Photo, date int) (domain.SetChannelPhotoResult, error)
SetPreHistoryHidden(ctx context.Context, userID, channelID int64, enabled bool) (domain.Channel, error)
SetParticipantsHidden(ctx context.Context, userID, channelID int64, enabled bool) (domain.Channel, error)
SetForum(ctx context.Context, userID, channelID int64, enabled, tabs bool) (domain.Channel, error)

View file

@ -70,6 +70,7 @@ func cloneChannelMessageAction(in *domain.ChannelMessageAction) *domain.ChannelM
out.StarGift = &g
}
out.Wallpaper = domain.CloneWallpaperPtr(in.Wallpaper)
out.Photo = domain.ClonePhotoPtr(in.Photo)
return &out
}

View file

@ -219,31 +219,56 @@ func (s *ChannelStore) ResolvePublicChannelUsername(_ context.Context, viewerUse
return domain.Channel{}, false, nil
}
func (s *ChannelStore) SetChannelPhoto(_ context.Context, userID, channelID int64, photo *domain.Photo) (domain.Channel, error) {
func (s *ChannelStore) SetChannelPhoto(_ context.Context, userID, channelID int64, photo *domain.Photo, date int) (domain.SetChannelPhotoResult, error) {
if userID == 0 || channelID == 0 {
return domain.Channel{}, domain.ErrChannelInvalid
return domain.SetChannelPhotoResult{}, domain.ErrChannelInvalid
}
if date == 0 {
date = int(time.Now().Unix())
}
s.mu.Lock()
defer s.mu.Unlock()
channel, err := s.channelForMemberLocked(userID, channelID)
if err != nil {
return domain.Channel{}, err
return domain.SetChannelPhotoResult{}, err
}
member := s.members[channelID][userID]
if !canChangeChannelInfo(member) {
return domain.Channel{}, domain.ErrChannelAdminRequired
return domain.SetChannelPhotoResult{}, domain.ErrChannelAdminRequired
}
var action domain.ChannelMessageAction
if photo != nil && photo.ID != 0 {
if channel.PhotoID == photo.ID {
return domain.SetChannelPhotoResult{}, domain.ErrChannelNotModified
}
action = domain.ChannelMessageAction{
Type: domain.ChannelActionChatEditPhoto,
Photo: domain.ClonePhotoPtr(photo),
}
channel.PhotoID = photo.ID
channel.PhotoDCID = photo.DCID
channel.PhotoStripped = domain.StrippedFromSizes(photo.Sizes)
} else {
if channel.PhotoID == 0 {
return domain.SetChannelPhotoResult{}, domain.ErrChannelNotModified
}
action = domain.ChannelMessageAction{Type: domain.ChannelActionChatDeletePhoto}
channel.PhotoID = 0
channel.PhotoDCID = 0
channel.PhotoStripped = nil
}
msg, event := s.appendChannelServiceMessageLocked(channelID, userID, date, action)
channel.TopMessageID = msg.ID
channel.Pts = event.Pts
s.channels[channelID] = channel
return channel, nil
s.upsertChannelDialogLocked(userID, channel, msg, true)
return domain.SetChannelPhotoResult{
Channel: cloneChannel(channel),
Message: cloneChannelMessage(msg),
Event: cloneChannelEvent(event),
Recipients: s.activeMemberIDsLocked(channelID, 0, 0),
Changed: true,
}, nil
}
func (s *ChannelStore) SetSignatures(_ context.Context, userID, channelID int64, enabled bool) (domain.Channel, error) {

View file

@ -361,18 +361,21 @@ func (s *ChannelStore) SetSignatures(ctx context.Context, userID, channelID int6
return channel, nil
}
// SetChannelPhoto 设置/清除频道头像(反范式列)。photo==nil 表示清除。
func (s *ChannelStore) SetChannelPhoto(ctx context.Context, userID, channelID int64, photo *domain.Photo) (domain.Channel, error) {
// SetChannelPhoto 设置/清除频道头像,并生成对应频道服务消息。photo==nil 表示清除。
func (s *ChannelStore) SetChannelPhoto(ctx context.Context, userID, channelID int64, photo *domain.Photo, date int) (domain.SetChannelPhotoResult, error) {
if userID == 0 || channelID == 0 {
return domain.Channel{}, domain.ErrChannelInvalid
return domain.SetChannelPhotoResult{}, domain.ErrChannelInvalid
}
beginner, ok := s.db.(txBeginner)
if !ok {
return domain.Channel{}, fmt.Errorf("set channel photo: db does not support transactions")
return domain.SetChannelPhotoResult{}, fmt.Errorf("set channel photo: db does not support transactions")
}
if date == 0 {
date = nowUnix()
}
tx, err := beginner.Begin(ctx)
if err != nil {
return domain.Channel{}, fmt.Errorf("begin set channel photo: %w", err)
return domain.SetChannelPhotoResult{}, fmt.Errorf("begin set channel photo: %w", err)
}
committed := false
defer func() {
@ -382,36 +385,68 @@ func (s *ChannelStore) SetChannelPhoto(ctx context.Context, userID, channelID in
}()
channel, member, err := s.getChannelForMember(ctx, tx, userID, channelID)
if err != nil {
return domain.Channel{}, err
return domain.SetChannelPhotoResult{}, err
}
if !canChangeChannelInfo(member) {
return domain.Channel{}, domain.ErrChannelAdminRequired
return domain.SetChannelPhotoResult{}, domain.ErrChannelAdminRequired
}
var (
photoID int64
dcID int
stripped []byte
action domain.ChannelMessageAction
)
if photo != nil && photo.ID != 0 {
if channel.PhotoID == photo.ID {
return domain.SetChannelPhotoResult{}, domain.ErrChannelNotModified
}
photoID = photo.ID
dcID = photo.DCID
stripped = domain.StrippedFromSizes(photo.Sizes)
action = domain.ChannelMessageAction{
Type: domain.ChannelActionChatEditPhoto,
Photo: domain.ClonePhotoPtr(photo),
}
} else {
if channel.PhotoID == 0 {
return domain.SetChannelPhotoResult{}, domain.ErrChannelNotModified
}
action = domain.ChannelMessageAction{Type: domain.ChannelActionChatDeletePhoto}
}
if stripped == nil {
stripped = []byte{}
}
if _, err := tx.Exec(ctx, `UPDATE channels SET photo_id = $2, photo_dc_id = $3, photo_stripped = $4, updated_at = now() WHERE id = $1`,
channelID, photoID, dcID, stripped); err != nil {
return domain.Channel{}, fmt.Errorf("update channel photo: %w", err)
return domain.SetChannelPhotoResult{}, fmt.Errorf("update channel photo: %w", err)
}
if s.rowCache != nil {
s.rowCache.delete(channelID)
}
channel.PhotoID = photoID
channel.PhotoDCID = dcID
channel.PhotoStripped = stripped
msg, event, err := s.insertServiceMessage(ctx, tx, channel, userID, date, action)
if err != nil {
return domain.SetChannelPhotoResult{}, err
}
channel.TopMessageID = msg.ID
channel.Pts = event.Pts
if err := upsertChannelDialogTx(ctx, tx, userID, channel, msg, msg.ID, 0); err != nil {
return domain.SetChannelPhotoResult{}, err
}
if err := tx.Commit(ctx); err != nil {
return domain.Channel{}, fmt.Errorf("commit set channel photo: %w", err)
return domain.SetChannelPhotoResult{}, fmt.Errorf("commit set channel photo: %w", err)
}
committed = true
return channel, nil
recipients, _ := s.ListActiveChannelMemberIDs(ctx, userID, channelID, 0)
return domain.SetChannelPhotoResult{
Channel: channel,
Message: msg,
Event: event,
Recipients: recipients,
Changed: true,
}, nil
}
func (s *ChannelStore) SetAutotranslation(ctx context.Context, userID, channelID int64, enabled bool) (domain.Channel, error) {

View file

@ -46,6 +46,7 @@ func cloneChannelMessageAction(action *domain.ChannelMessageAction) *domain.Chan
clone.StarGift = &g
}
clone.Wallpaper = domain.CloneWallpaperPtr(action.Wallpaper)
clone.Photo = domain.ClonePhotoPtr(action.Photo)
return &clone
}