github.com/status-im/status-go@v1.1.0/protocol/messenger_group_chat.go (about)

     1  package protocol
     2  
     3  import (
     4  	"context"
     5  	"crypto/ecdsa"
     6  	"encoding/hex"
     7  	"errors"
     8  
     9  	"github.com/golang/protobuf/proto"
    10  	"go.uber.org/zap"
    11  
    12  	"github.com/status-im/status-go/eth-node/crypto"
    13  	"github.com/status-im/status-go/eth-node/types"
    14  	"github.com/status-im/status-go/images"
    15  	"github.com/status-im/status-go/protocol/common"
    16  	"github.com/status-im/status-go/protocol/protobuf"
    17  	"github.com/status-im/status-go/protocol/requests"
    18  	v1protocol "github.com/status-im/status-go/protocol/v1"
    19  )
    20  
    21  var ErrGroupChatAddedContacts = errors.New("group-chat: can't add members who are not mutual contacts")
    22  
    23  func (m *Messenger) validateAddedGroupMembers(members []string) error {
    24  	for _, memberPubkey := range members {
    25  		contactID, err := contactIDFromPublicKeyString(memberPubkey)
    26  		if err != nil {
    27  			return err
    28  		}
    29  
    30  		contact, _ := m.allContacts.Load(contactID)
    31  		if contact == nil || !contact.mutual() {
    32  			return ErrGroupChatAddedContacts
    33  		}
    34  	}
    35  	return nil
    36  }
    37  
    38  func (m *Messenger) CreateGroupChatWithMembers(ctx context.Context, name string, members []string) (*MessengerResponse, error) {
    39  	var convertedKeyMembers []string
    40  	for _, m := range members {
    41  		k, err := requests.ConvertCompressedToLegacyKey(m)
    42  		if err != nil {
    43  			return nil, err
    44  		}
    45  		convertedKeyMembers = append(convertedKeyMembers, k)
    46  
    47  	}
    48  	if err := m.validateAddedGroupMembers(convertedKeyMembers); err != nil {
    49  		return nil, err
    50  	}
    51  
    52  	var response MessengerResponse
    53  	logger := m.logger.With(zap.String("site", "CreateGroupChatWithMembers"))
    54  	logger.Info("Creating group chat", zap.String("name", name), zap.Any("members", convertedKeyMembers))
    55  	chat := CreateGroupChat(m.getTimesource())
    56  
    57  	clock, _ := chat.NextClockAndTimestamp(m.getTimesource())
    58  
    59  	group, err := v1protocol.NewGroupWithCreator(name, chat.Color, clock, m.identity)
    60  	if err != nil {
    61  		return nil, err
    62  	}
    63  	chat.LastClockValue = clock
    64  
    65  	chat.updateChatFromGroupMembershipChanges(group)
    66  	chat.Joined = int64(m.getTimesource().GetCurrentTime())
    67  
    68  	clock, _ = chat.NextClockAndTimestamp(m.getTimesource())
    69  
    70  	// Add members
    71  	if len(convertedKeyMembers) > 0 {
    72  		event := v1protocol.NewMembersAddedEvent(convertedKeyMembers, clock)
    73  		event.ChatID = chat.ID
    74  		err = event.Sign(m.identity)
    75  		if err != nil {
    76  			return nil, err
    77  		}
    78  
    79  		err = group.ProcessEvent(event)
    80  		if err != nil {
    81  			return nil, err
    82  		}
    83  	}
    84  
    85  	recipients, err := stringSliceToPublicKeys(group.Members())
    86  
    87  	if err != nil {
    88  		return nil, err
    89  	}
    90  
    91  	encodedMessage, err := m.sender.EncodeMembershipUpdate(group, nil)
    92  	if err != nil {
    93  		return nil, err
    94  	}
    95  
    96  	m.allChats.Store(chat.ID, &chat)
    97  
    98  	_, err = m.dispatchMessage(ctx, common.RawMessage{
    99  		LocalChatID: chat.ID,
   100  		Payload:     encodedMessage,
   101  		MessageType: protobuf.ApplicationMetadataMessage_MEMBERSHIP_UPDATE_MESSAGE,
   102  		Recipients:  recipients,
   103  	})
   104  
   105  	if err != nil {
   106  		return nil, err
   107  	}
   108  
   109  	chat.updateChatFromGroupMembershipChanges(group)
   110  
   111  	return m.addMessagesAndChat(&chat, buildSystemMessages(chat.MembershipUpdates, m.systemMessagesTranslations), &response)
   112  }
   113  
   114  func (m *Messenger) CreateGroupChatFromInvitation(name string, chatID string, adminPK string) (*MessengerResponse, error) {
   115  	var response MessengerResponse
   116  	logger := m.logger.With(zap.String("site", "CreateGroupChatFromInvitation"))
   117  	logger.Info("Creating group chat from invitation", zap.String("name", name))
   118  	chat := CreateGroupChat(m.getTimesource())
   119  	chat.ID = chatID
   120  	chat.Name = name
   121  	chat.InvitationAdmin = adminPK
   122  
   123  	response.AddChat(&chat)
   124  
   125  	return &response, m.saveChat(&chat)
   126  }
   127  
   128  type removeMembersFromGroupChatResponse struct {
   129  	oldRecipients   []*ecdsa.PublicKey
   130  	group           *v1protocol.Group
   131  	encodedProtobuf []byte
   132  }
   133  
   134  func (m *Messenger) removeMembersFromGroupChat(ctx context.Context, chat *Chat, members []string) (*removeMembersFromGroupChatResponse, error) {
   135  	chatID := chat.ID
   136  	logger := m.logger.With(zap.String("site", "RemoveMembersFromGroupChat"))
   137  	logger.Info("Removing members form group chat", zap.String("chatID", chatID), zap.Any("members", members))
   138  	group, err := newProtocolGroupFromChat(chat)
   139  	if err != nil {
   140  		return nil, err
   141  	}
   142  
   143  	// We save the initial recipients as we want to send updates to also
   144  	// the members kicked out
   145  	oldRecipients, err := stringSliceToPublicKeys(group.Members())
   146  	if err != nil {
   147  		return nil, err
   148  	}
   149  
   150  	clock, _ := chat.NextClockAndTimestamp(m.getTimesource())
   151  
   152  	for _, member := range members {
   153  		// Remove member
   154  		event := v1protocol.NewMemberRemovedEvent(member, clock)
   155  		event.ChatID = chat.ID
   156  		err = event.Sign(m.identity)
   157  		if err != nil {
   158  			return nil, err
   159  		}
   160  
   161  		err = group.ProcessEvent(event)
   162  		if err != nil {
   163  			return nil, err
   164  		}
   165  	}
   166  
   167  	encoded, err := m.sender.EncodeMembershipUpdate(group, nil)
   168  	if err != nil {
   169  		return nil, err
   170  	}
   171  
   172  	return &removeMembersFromGroupChatResponse{
   173  		oldRecipients:   oldRecipients,
   174  		group:           group,
   175  		encodedProtobuf: encoded,
   176  	}, nil
   177  }
   178  
   179  func (m *Messenger) RemoveMembersFromGroupChat(ctx context.Context, chatID string, members []string) (*MessengerResponse, error) {
   180  	var response MessengerResponse
   181  
   182  	chat, ok := m.allChats.Load(chatID)
   183  	if !ok {
   184  		return nil, ErrChatNotFound
   185  	}
   186  
   187  	removeMembersResponse, err := m.removeMembersFromGroupChat(ctx, chat, members)
   188  	if err != nil {
   189  		return nil, err
   190  	}
   191  
   192  	_, err = m.dispatchMessage(ctx, common.RawMessage{
   193  		LocalChatID: chat.ID,
   194  		Payload:     removeMembersResponse.encodedProtobuf,
   195  		MessageType: protobuf.ApplicationMetadataMessage_MEMBERSHIP_UPDATE_MESSAGE,
   196  		Recipients:  removeMembersResponse.oldRecipients,
   197  	})
   198  	if err != nil {
   199  		return nil, err
   200  	}
   201  
   202  	chat.updateChatFromGroupMembershipChanges(removeMembersResponse.group)
   203  
   204  	return m.addMessagesAndChat(chat, buildSystemMessages(chat.MembershipUpdates, m.systemMessagesTranslations), &response)
   205  }
   206  
   207  func (m *Messenger) AddMembersToGroupChat(ctx context.Context, chatID string, members []string) (*MessengerResponse, error) {
   208  	if err := m.validateAddedGroupMembers(members); err != nil {
   209  		return nil, err
   210  	}
   211  
   212  	var response MessengerResponse
   213  	logger := m.logger.With(zap.String("site", "AddMembersFromGroupChat"))
   214  	logger.Info("Adding members form group chat", zap.String("chatID", chatID), zap.Any("members", members))
   215  	chat, ok := m.allChats.Load(chatID)
   216  	if !ok {
   217  		return nil, ErrChatNotFound
   218  	}
   219  
   220  	group, err := newProtocolGroupFromChat(chat)
   221  	if err != nil {
   222  		return nil, err
   223  	}
   224  
   225  	clock, _ := chat.NextClockAndTimestamp(m.getTimesource())
   226  	// Add members
   227  	event := v1protocol.NewMembersAddedEvent(members, clock)
   228  	event.ChatID = chat.ID
   229  	err = event.Sign(m.identity)
   230  	if err != nil {
   231  		return nil, err
   232  	}
   233  
   234  	//approve invitations
   235  	for _, member := range members {
   236  		logger.Info("ApproveInvitationByChatIdAndFrom", zap.String("chatID", chatID), zap.Any("member", member))
   237  
   238  		groupChatInvitation := &GroupChatInvitation{
   239  			GroupChatInvitation: &protobuf.GroupChatInvitation{
   240  				ChatId: chat.ID,
   241  			},
   242  			From: member,
   243  		}
   244  
   245  		groupChatInvitation, err = m.persistence.InvitationByID(groupChatInvitation.ID())
   246  		if err != nil && err != common.ErrRecordNotFound {
   247  			return nil, err
   248  		}
   249  		if groupChatInvitation != nil {
   250  			groupChatInvitation.State = protobuf.GroupChatInvitation_APPROVED
   251  
   252  			err := m.persistence.SaveInvitation(groupChatInvitation)
   253  			if err != nil {
   254  				return nil, err
   255  			}
   256  			response.Invitations = append(response.Invitations, groupChatInvitation)
   257  		}
   258  	}
   259  
   260  	err = group.ProcessEvent(event)
   261  	if err != nil {
   262  		return nil, err
   263  	}
   264  
   265  	recipients, err := stringSliceToPublicKeys(group.Members())
   266  	if err != nil {
   267  		return nil, err
   268  	}
   269  
   270  	encodedMessage, err := m.sender.EncodeMembershipUpdate(group, nil)
   271  	if err != nil {
   272  		return nil, err
   273  	}
   274  	_, err = m.dispatchMessage(ctx, common.RawMessage{
   275  		LocalChatID: chat.ID,
   276  		Payload:     encodedMessage,
   277  		MessageType: protobuf.ApplicationMetadataMessage_MEMBERSHIP_UPDATE_MESSAGE,
   278  		Recipients:  recipients,
   279  	})
   280  
   281  	if err != nil {
   282  		return nil, err
   283  	}
   284  
   285  	chat.updateChatFromGroupMembershipChanges(group)
   286  
   287  	return m.addMessagesAndChat(chat, buildSystemMessages([]v1protocol.MembershipUpdateEvent{event}, m.systemMessagesTranslations), &response)
   288  }
   289  
   290  func (m *Messenger) ChangeGroupChatName(ctx context.Context, chatID string, name string) (*MessengerResponse, error) {
   291  	logger := m.logger.With(zap.String("site", "ChangeGroupChatName"))
   292  	logger.Info("Changing group chat name", zap.String("chatID", chatID), zap.String("name", name))
   293  
   294  	chat, ok := m.allChats.Load(chatID)
   295  	if !ok {
   296  		return nil, ErrChatNotFound
   297  	}
   298  
   299  	group, err := newProtocolGroupFromChat(chat)
   300  	if err != nil {
   301  		return nil, err
   302  	}
   303  
   304  	clock, _ := chat.NextClockAndTimestamp(m.getTimesource())
   305  	// Change name
   306  	event := v1protocol.NewNameChangedEvent(name, clock)
   307  	event.ChatID = chat.ID
   308  	err = event.Sign(m.identity)
   309  	if err != nil {
   310  		return nil, err
   311  	}
   312  
   313  	// Update in-memory group
   314  	err = group.ProcessEvent(event)
   315  	if err != nil {
   316  		return nil, err
   317  	}
   318  
   319  	recipients, err := stringSliceToPublicKeys(group.Members())
   320  	if err != nil {
   321  		return nil, err
   322  	}
   323  
   324  	encodedMessage, err := m.sender.EncodeMembershipUpdate(group, nil)
   325  	if err != nil {
   326  		return nil, err
   327  	}
   328  	_, err = m.dispatchMessage(ctx, common.RawMessage{
   329  		LocalChatID: chat.ID,
   330  		Payload:     encodedMessage,
   331  		MessageType: protobuf.ApplicationMetadataMessage_MEMBERSHIP_UPDATE_MESSAGE,
   332  		Recipients:  recipients,
   333  	})
   334  
   335  	if err != nil {
   336  		return nil, err
   337  	}
   338  
   339  	chat.updateChatFromGroupMembershipChanges(group)
   340  
   341  	var response MessengerResponse
   342  
   343  	return m.addMessagesAndChat(chat, buildSystemMessages([]v1protocol.MembershipUpdateEvent{event}, m.systemMessagesTranslations), &response)
   344  }
   345  
   346  func (m *Messenger) EditGroupChat(ctx context.Context, chatID string, name string, color string, image images.CroppedImage) (*MessengerResponse, error) {
   347  	logger := m.logger.With(zap.String("site", "EditGroupChat"))
   348  	logger.Info("Editing group chat details", zap.String("chatID", chatID), zap.String("name", name), zap.String("color", color))
   349  
   350  	chat, ok := m.allChats.Load(chatID)
   351  	if !ok {
   352  		return nil, ErrChatNotFound
   353  	}
   354  
   355  	group, err := newProtocolGroupFromChat(chat)
   356  	if err != nil {
   357  		return nil, err
   358  	}
   359  
   360  	signAndProcessEvent := func(m *Messenger, event *v1protocol.MembershipUpdateEvent) error {
   361  		err := event.Sign(m.identity)
   362  		if err != nil {
   363  			return err
   364  		}
   365  
   366  		err = group.ProcessEvent(*event)
   367  		if err != nil {
   368  			return err
   369  		}
   370  
   371  		return nil
   372  	}
   373  
   374  	var events []v1protocol.MembershipUpdateEvent
   375  
   376  	if chat.Name != name {
   377  		clock, _ := chat.NextClockAndTimestamp(m.getTimesource())
   378  		event := v1protocol.NewNameChangedEvent(name, clock)
   379  		event.ChatID = chat.ID
   380  		err = signAndProcessEvent(m, &event)
   381  		if err != nil {
   382  			return nil, err
   383  		}
   384  		events = append(events, event)
   385  	}
   386  
   387  	if chat.Color != color {
   388  		clock, _ := chat.NextClockAndTimestamp(m.getTimesource())
   389  		event := v1protocol.NewColorChangedEvent(color, clock)
   390  		event.ChatID = chat.ID
   391  		err = signAndProcessEvent(m, &event)
   392  		if err != nil {
   393  			return nil, err
   394  		}
   395  		events = append(events, event)
   396  	}
   397  
   398  	if len(image.ImagePath) > 0 {
   399  		payload, err := images.OpenAndAdjustImage(image, true)
   400  
   401  		if err != nil {
   402  			return nil, err
   403  		}
   404  
   405  		// prepare event
   406  		clock, _ := chat.NextClockAndTimestamp(m.getTimesource())
   407  		event := v1protocol.NewImageChangedEvent(payload, clock)
   408  		event.ChatID = chat.ID
   409  		err = signAndProcessEvent(m, &event)
   410  		if err != nil {
   411  			return nil, err
   412  		}
   413  		events = append(events, event)
   414  	}
   415  
   416  	recipients, err := stringSliceToPublicKeys(group.Members())
   417  	if err != nil {
   418  		return nil, err
   419  	}
   420  
   421  	encodedMessage, err := m.sender.EncodeMembershipUpdate(group, nil)
   422  	if err != nil {
   423  		return nil, err
   424  	}
   425  	_, err = m.dispatchMessage(ctx, common.RawMessage{
   426  		LocalChatID: chat.ID,
   427  		Payload:     encodedMessage,
   428  		MessageType: protobuf.ApplicationMetadataMessage_MEMBERSHIP_UPDATE_MESSAGE,
   429  		Recipients:  recipients,
   430  	})
   431  
   432  	if err != nil {
   433  		return nil, err
   434  	}
   435  
   436  	chat.updateChatFromGroupMembershipChanges(group)
   437  
   438  	var response MessengerResponse
   439  
   440  	return m.addMessagesAndChat(chat, buildSystemMessages(events, m.systemMessagesTranslations), &response)
   441  }
   442  
   443  func (m *Messenger) SendGroupChatInvitationRequest(ctx context.Context, chatID string, adminPK string,
   444  	message string) (*MessengerResponse, error) {
   445  	logger := m.logger.With(zap.String("site", "SendGroupChatInvitationRequest"))
   446  	logger.Info("Sending group chat invitation request", zap.String("chatID", chatID),
   447  		zap.String("adminPK", adminPK), zap.String("message", message))
   448  
   449  	var response MessengerResponse
   450  
   451  	// Get chat and clock
   452  	chat, ok := m.allChats.Load(chatID)
   453  	if !ok {
   454  		return nil, ErrChatNotFound
   455  	}
   456  	clock, _ := chat.NextClockAndTimestamp(m.getTimesource())
   457  
   458  	invitationR := &GroupChatInvitation{
   459  		GroupChatInvitation: &protobuf.GroupChatInvitation{
   460  			Clock:               clock,
   461  			ChatId:              chatID,
   462  			IntroductionMessage: message,
   463  			State:               protobuf.GroupChatInvitation_REQUEST,
   464  		},
   465  		From: types.EncodeHex(crypto.FromECDSAPub(&m.identity.PublicKey)),
   466  	}
   467  
   468  	encodedMessage, err := proto.Marshal(invitationR.GetProtobuf())
   469  	if err != nil {
   470  		return nil, err
   471  	}
   472  
   473  	spec := common.RawMessage{
   474  		LocalChatID: adminPK,
   475  		Payload:     encodedMessage,
   476  		MessageType: protobuf.ApplicationMetadataMessage_GROUP_CHAT_INVITATION,
   477  		ResendType:  common.ResendTypeDataSync,
   478  	}
   479  
   480  	pkey, err := hex.DecodeString(adminPK[2:])
   481  	if err != nil {
   482  		return nil, err
   483  	}
   484  	// Safety check, make sure is well formed
   485  	adminpk, err := crypto.UnmarshalPubkey(pkey)
   486  	if err != nil {
   487  		return nil, err
   488  	}
   489  
   490  	id, err := m.sender.SendPrivate(ctx, adminpk, &spec)
   491  	if err != nil {
   492  		return nil, err
   493  	}
   494  
   495  	spec.ID = types.EncodeHex(id)
   496  	spec.SendCount++
   497  	err = m.persistence.SaveRawMessage(&spec)
   498  	if err != nil {
   499  		return nil, err
   500  	}
   501  
   502  	response.Invitations = []*GroupChatInvitation{invitationR}
   503  
   504  	err = m.persistence.SaveInvitation(invitationR)
   505  	if err != nil {
   506  		return nil, err
   507  	}
   508  
   509  	return &response, nil
   510  }
   511  
   512  func (m *Messenger) GetGroupChatInvitations() ([]*GroupChatInvitation, error) {
   513  	return m.persistence.GetGroupChatInvitations()
   514  }
   515  
   516  func (m *Messenger) SendGroupChatInvitationRejection(ctx context.Context, invitationRequestID string) (*MessengerResponse, error) {
   517  	logger := m.logger.With(zap.String("site", "SendGroupChatInvitationRejection"))
   518  	logger.Info("Sending group chat invitation reject", zap.String("invitationRequestID", invitationRequestID))
   519  
   520  	invitationR, err := m.persistence.InvitationByID(invitationRequestID)
   521  	if err != nil {
   522  		return nil, err
   523  	}
   524  
   525  	invitationR.State = protobuf.GroupChatInvitation_REJECTED
   526  
   527  	// Get chat and clock
   528  	chat, ok := m.allChats.Load(invitationR.ChatId)
   529  	if !ok {
   530  		return nil, ErrChatNotFound
   531  	}
   532  	clock, _ := chat.NextClockAndTimestamp(m.getTimesource())
   533  
   534  	invitationR.Clock = clock
   535  
   536  	encodedMessage, err := proto.Marshal(invitationR.GetProtobuf())
   537  	if err != nil {
   538  		return nil, err
   539  	}
   540  
   541  	spec := common.RawMessage{
   542  		LocalChatID: invitationR.From,
   543  		Payload:     encodedMessage,
   544  		MessageType: protobuf.ApplicationMetadataMessage_GROUP_CHAT_INVITATION,
   545  		ResendType:  common.ResendTypeDataSync,
   546  	}
   547  
   548  	pkey, err := hex.DecodeString(invitationR.From[2:])
   549  	if err != nil {
   550  		return nil, err
   551  	}
   552  	// Safety check, make sure is well formed
   553  	userpk, err := crypto.UnmarshalPubkey(pkey)
   554  	if err != nil {
   555  		return nil, err
   556  	}
   557  
   558  	id, err := m.sender.SendPrivate(ctx, userpk, &spec)
   559  	if err != nil {
   560  		return nil, err
   561  	}
   562  
   563  	spec.ID = types.EncodeHex(id)
   564  	spec.SendCount++
   565  	err = m.persistence.SaveRawMessage(&spec)
   566  	if err != nil {
   567  		return nil, err
   568  	}
   569  
   570  	var response MessengerResponse
   571  
   572  	response.Invitations = []*GroupChatInvitation{invitationR}
   573  
   574  	err = m.persistence.SaveInvitation(invitationR)
   575  	if err != nil {
   576  		return nil, err
   577  	}
   578  
   579  	return &response, nil
   580  }
   581  
   582  func (m *Messenger) AddAdminsToGroupChat(ctx context.Context, chatID string, members []string) (*MessengerResponse, error) {
   583  	var response MessengerResponse
   584  	logger := m.logger.With(zap.String("site", "AddAdminsToGroupChat"))
   585  	logger.Info("Add admins to group chat", zap.String("chatID", chatID), zap.Any("members", members))
   586  
   587  	chat, ok := m.allChats.Load(chatID)
   588  	if !ok {
   589  		return nil, ErrChatNotFound
   590  	}
   591  
   592  	group, err := newProtocolGroupFromChat(chat)
   593  	if err != nil {
   594  		return nil, err
   595  	}
   596  
   597  	clock, _ := chat.NextClockAndTimestamp(m.getTimesource())
   598  	// Add members
   599  	event := v1protocol.NewAdminsAddedEvent(members, clock)
   600  	event.ChatID = chat.ID
   601  	err = event.Sign(m.identity)
   602  	if err != nil {
   603  		return nil, err
   604  	}
   605  
   606  	err = group.ProcessEvent(event)
   607  	if err != nil {
   608  		return nil, err
   609  	}
   610  
   611  	recipients, err := stringSliceToPublicKeys(group.Members())
   612  	if err != nil {
   613  		return nil, err
   614  	}
   615  
   616  	encodedMessage, err := m.sender.EncodeMembershipUpdate(group, nil)
   617  	if err != nil {
   618  		return nil, err
   619  	}
   620  	_, err = m.dispatchMessage(ctx, common.RawMessage{
   621  		LocalChatID: chat.ID,
   622  		Payload:     encodedMessage,
   623  		MessageType: protobuf.ApplicationMetadataMessage_MEMBERSHIP_UPDATE_MESSAGE,
   624  		Recipients:  recipients,
   625  	})
   626  
   627  	if err != nil {
   628  		return nil, err
   629  	}
   630  
   631  	chat.updateChatFromGroupMembershipChanges(group)
   632  	return m.addMessagesAndChat(chat, buildSystemMessages([]v1protocol.MembershipUpdateEvent{event}, m.systemMessagesTranslations), &response)
   633  }
   634  
   635  // Kept only for backward compatibility (auto-join), explicit join has been removed
   636  func (m *Messenger) ConfirmJoiningGroup(ctx context.Context, chatID string) (*MessengerResponse, error) {
   637  	var response MessengerResponse
   638  
   639  	chat, ok := m.allChats.Load(chatID)
   640  	if !ok {
   641  		return nil, ErrChatNotFound
   642  	}
   643  
   644  	_, err := m.Join(chat)
   645  	if err != nil {
   646  		return nil, err
   647  	}
   648  
   649  	group, err := newProtocolGroupFromChat(chat)
   650  	if err != nil {
   651  		return nil, err
   652  	}
   653  	clock, _ := chat.NextClockAndTimestamp(m.getTimesource())
   654  	event := v1protocol.NewMemberJoinedEvent(
   655  		clock,
   656  	)
   657  	event.ChatID = chat.ID
   658  	err = event.Sign(m.identity)
   659  	if err != nil {
   660  		return nil, err
   661  	}
   662  
   663  	err = group.ProcessEvent(event)
   664  	if err != nil {
   665  		return nil, err
   666  	}
   667  
   668  	recipients, err := stringSliceToPublicKeys(group.Members())
   669  	if err != nil {
   670  		return nil, err
   671  	}
   672  
   673  	encodedMessage, err := m.sender.EncodeMembershipUpdate(group, nil)
   674  	if err != nil {
   675  		return nil, err
   676  	}
   677  	_, err = m.dispatchMessage(ctx, common.RawMessage{
   678  		LocalChatID: chat.ID,
   679  		Payload:     encodedMessage,
   680  		MessageType: protobuf.ApplicationMetadataMessage_MEMBERSHIP_UPDATE_MESSAGE,
   681  		Recipients:  recipients,
   682  	})
   683  	if err != nil {
   684  		return nil, err
   685  	}
   686  
   687  	chat.updateChatFromGroupMembershipChanges(group)
   688  	chat.Joined = int64(m.getTimesource().GetCurrentTime())
   689  
   690  	return m.addMessagesAndChat(chat, buildSystemMessages([]v1protocol.MembershipUpdateEvent{event}, m.systemMessagesTranslations), &response)
   691  }
   692  
   693  func (m *Messenger) leaveGroupChat(ctx context.Context, response *MessengerResponse, chatID string, remove bool, shouldBeSynced bool) (*MessengerResponse, error) {
   694  	chat, ok := m.allChats.Load(chatID)
   695  	if !ok {
   696  		return nil, ErrChatNotFound
   697  	}
   698  
   699  	amIMember := chat.HasMember(common.PubkeyToHex(&m.identity.PublicKey))
   700  
   701  	if amIMember {
   702  		chat.RemoveMember(common.PubkeyToHex(&m.identity.PublicKey))
   703  
   704  		group, err := newProtocolGroupFromChat(chat)
   705  		if err != nil {
   706  			return nil, err
   707  		}
   708  		clock, _ := chat.NextClockAndTimestamp(m.getTimesource())
   709  		event := v1protocol.NewMemberRemovedEvent(
   710  			contactIDFromPublicKey(&m.identity.PublicKey),
   711  			clock,
   712  		)
   713  		event.ChatID = chat.ID
   714  		err = event.Sign(m.identity)
   715  		if err != nil {
   716  			return nil, err
   717  		}
   718  
   719  		err = group.ProcessEvent(event)
   720  		if err != nil {
   721  			return nil, err
   722  		}
   723  
   724  		recipients, err := stringSliceToPublicKeys(group.Members())
   725  		if err != nil {
   726  			return nil, err
   727  		}
   728  
   729  		encodedMessage, err := m.sender.EncodeMembershipUpdate(group, nil)
   730  		if err != nil {
   731  			return nil, err
   732  		}
   733  
   734  		// shouldBeSynced is false if we got here because a synced client has already
   735  		// sent the leave group message. In that case we don't need to send it again.
   736  		if shouldBeSynced {
   737  			_, err = m.dispatchMessage(ctx, common.RawMessage{
   738  				LocalChatID: chat.ID,
   739  				Payload:     encodedMessage,
   740  				MessageType: protobuf.ApplicationMetadataMessage_MEMBERSHIP_UPDATE_MESSAGE,
   741  				Recipients:  recipients,
   742  			})
   743  			if err != nil {
   744  				return nil, err
   745  			}
   746  		}
   747  
   748  		chat.updateChatFromGroupMembershipChanges(group)
   749  		response.AddMessages(buildSystemMessages([]v1protocol.MembershipUpdateEvent{event}, m.systemMessagesTranslations))
   750  		err = m.persistence.SaveMessages(response.Messages())
   751  		if err != nil {
   752  			return nil, err
   753  		}
   754  	}
   755  
   756  	if remove {
   757  		chat.Active = false
   758  	}
   759  
   760  	if remove && shouldBeSynced {
   761  		err := m.syncChatRemoving(ctx, chat.ID, m.dispatchMessage)
   762  		if err != nil {
   763  			return nil, err
   764  		}
   765  	}
   766  
   767  	response.AddChat(chat)
   768  
   769  	return response, m.saveChat(chat)
   770  }
   771  
   772  func (m *Messenger) LeaveGroupChat(ctx context.Context, chatID string, remove bool) (*MessengerResponse, error) {
   773  	_, err := m.DismissAllActivityCenterNotificationsFromChatID(ctx, chatID, m.GetCurrentTimeInMillis())
   774  	if err != nil {
   775  		return nil, err
   776  	}
   777  	var response MessengerResponse
   778  	return m.leaveGroupChat(ctx, &response, chatID, remove, true)
   779  }
   780  
   781  // Decline all pending group invites from a user
   782  func (m *Messenger) DeclineAllPendingGroupInvitesFromUser(ctx context.Context, response *MessengerResponse, userPublicKey string) (*MessengerResponse, error) {
   783  
   784  	// Decline group invites from active chats
   785  	chats, err := m.persistence.Chats()
   786  	if err != nil {
   787  		return nil, err
   788  	}
   789  
   790  	for _, chat := range chats {
   791  		if chat.ChatType == ChatTypePrivateGroupChat &&
   792  			chat.ReceivedInvitationAdmin == userPublicKey &&
   793  			chat.Joined == 0 && chat.Active {
   794  			response, err = m.leaveGroupChat(ctx, response, chat.ID, true, true)
   795  			if err != nil {
   796  				return nil, err
   797  			}
   798  		}
   799  	}
   800  
   801  	// Decline group invites from activity center notifications
   802  	notifications, err := m.AcceptActivityCenterNotificationsForInvitesFromUser(ctx, userPublicKey, m.GetCurrentTimeInMillis())
   803  	if err != nil {
   804  		return nil, err
   805  	}
   806  
   807  	for _, notification := range notifications {
   808  		response, err = m.leaveGroupChat(ctx, response, notification.ChatID, true, true)
   809  		if err != nil {
   810  			return nil, err
   811  		}
   812  	}
   813  	return response, nil
   814  }