Fix: mutual state messages behaviour (#3640)

* feat: don't remove sent mutual state messages on accepting a CR

* fix: don't send mutual state message for a new contact

* chore: move mutual state messages to `addContact`

* fix: use one chat for mutual state messages and contact requests

* fix: change `added` mutual state updatede messages to `accepted`

* feat: Use different content type for each mutual state event system message

* chore: use constants for mutual event system messages test, review fixes

* chore: fix tests related to local contacts map
This commit is contained in:
Mikhail Rogachev 2023-07-12 19:12:58 +04:00 committed by GitHub
parent 244b4273de
commit 8bcc493477
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 229 additions and 276 deletions

View File

@ -303,8 +303,10 @@ func ValidateReceivedChatMessage(message *protobuf.ChatMessage, whisperTimestamp
return errors.New("private group system message content type not allowed") return errors.New("private group system message content type not allowed")
} }
if message.ContentType == protobuf.ChatMessage_SYSTEM_MESSAGE_MUTUAL_STATE_UPDATE { if message.ContentType == protobuf.ChatMessage_SYSTEM_MESSAGE_MUTUAL_EVENT_SENT ||
return errors.New("mutual state update system message content type not allowed") message.ContentType == protobuf.ChatMessage_SYSTEM_MESSAGE_MUTUAL_EVENT_ACCEPTED ||
message.ContentType == protobuf.ChatMessage_SYSTEM_MESSAGE_MUTUAL_EVENT_REMOVED {
return errors.New("mutual state event system message content type not allowed")
} }
if err := ValidateDisplayName(&message.DisplayName); err != nil { if err := ValidateDisplayName(&message.DisplayName); err != nil {

View File

@ -468,7 +468,7 @@ func (s *MessengerBackupSuite) TestBackupContactsGreaterThanBatch() {
) )
s.Require().NoError(err) s.Require().NoError(err)
s.Require().Less(BackupContactsPerBatch*2, len(bob2.Contacts())) s.Require().Equal(BackupContactsPerBatch*2, len(bob2.Contacts()))
s.Require().Len(bob2.AddedContacts(), BackupContactsPerBatch*2) s.Require().Len(bob2.AddedContacts(), BackupContactsPerBatch*2)
} }

View File

@ -3,6 +3,7 @@ package protocol
import ( import (
"context" "context"
"crypto/ecdsa" "crypto/ecdsa"
"fmt"
"testing" "testing"
"github.com/stretchr/testify/suite" "github.com/stretchr/testify/suite"
@ -74,18 +75,19 @@ func (s *MessengerContactRequestSuite) sendContactRequest(request *requests.Send
// Check CR and mutual state update messages // Check CR and mutual state update messages
s.Require().Len(resp.Messages(), 2) s.Require().Len(resp.Messages(), 2)
contactRequest := s.findFirstByContentType(resp.Messages(), protobuf.ChatMessage_CONTACT_REQUEST) mutualStateUpdate := s.findFirstByContentType(resp.Messages(), protobuf.ChatMessage_SYSTEM_MESSAGE_MUTUAL_EVENT_SENT)
s.Require().NotNil(contactRequest)
mutualStateUpdate := s.findFirstByContentType(resp.Messages(), protobuf.ChatMessage_SYSTEM_MESSAGE_MUTUAL_STATE_UPDATE)
s.Require().NotNil(mutualStateUpdate) s.Require().NotNil(mutualStateUpdate)
s.Require().Equal(common.ContactRequestStatePending, contactRequest.ContactRequestState)
s.Require().Equal(request.Message, contactRequest.Text)
s.Require().NotNil(mutualStateUpdate.ID) s.Require().NotNil(mutualStateUpdate.ID)
s.Require().Equal(mutualStateUpdate.From, messenger.myHexIdentity()) s.Require().Equal(mutualStateUpdate.From, messenger.myHexIdentity())
s.Require().Equal(mutualStateUpdate.ChatId, request.ID) s.Require().Equal(mutualStateUpdate.ChatId, request.ID)
s.Require().Equal(mutualStateUpdate.Text, "You sent a contact request to @"+request.ID) s.Require().Equal(mutualStateUpdate.Text, fmt.Sprintf(outgoingMutualStateEventSentDefaultText, request.ID))
contactRequest := s.findFirstByContentType(resp.Messages(), protobuf.ChatMessage_CONTACT_REQUEST)
s.Require().NotNil(contactRequest)
s.Require().Equal(common.ContactRequestStatePending, contactRequest.ContactRequestState)
s.Require().Equal(request.Message, contactRequest.Text)
// Check pending notification // Check pending notification
s.Require().Len(resp.ActivityCenterNotifications(), 1) s.Require().Len(resp.ActivityCenterNotifications(), 1)
@ -112,13 +114,6 @@ func (s *MessengerContactRequestSuite) sendContactRequest(request *requests.Send
// Check contact's primary name matches notifiaction's name // Check contact's primary name matches notifiaction's name
s.Require().Equal(resp.ActivityCenterNotifications()[0].Name, contacts[0].PrimaryName()) s.Require().Equal(resp.ActivityCenterNotifications()[0].Name, contacts[0].PrimaryName())
// Make sure update message was saved properly
mutualStateUpdateFromDb, err := messenger.persistence.MessageByID(mutualStateUpdate.ID)
s.Require().NoError(err)
s.Require().Equal(mutualStateUpdateFromDb.From, messenger.myHexIdentity())
s.Require().Equal(mutualStateUpdateFromDb.ChatId, request.ID)
s.Require().Equal(mutualStateUpdateFromDb.Text, "You sent a contact request to @"+request.ID)
} }
func (s *MessengerContactRequestSuite) receiveContactRequest(messageText string, theirMessenger *Messenger) *common.Message { func (s *MessengerContactRequestSuite) receiveContactRequest(messageText string, theirMessenger *Messenger) *common.Message {
@ -126,7 +121,7 @@ func (s *MessengerContactRequestSuite) receiveContactRequest(messageText string,
resp, err := WaitOnMessengerResponse( resp, err := WaitOnMessengerResponse(
theirMessenger, theirMessenger,
func(r *MessengerResponse) bool { func(r *MessengerResponse) bool {
return len(r.Contacts) == 1 && len(r.Messages()) == 2 && len(r.ActivityCenterNotifications()) == 1 return len(r.Contacts) == 1 && len(r.Messages()) >= 2 && len(r.ActivityCenterNotifications()) == 1
}, },
"no messages", "no messages",
) )
@ -141,14 +136,15 @@ func (s *MessengerContactRequestSuite) receiveContactRequest(messageText string,
contactRequest := s.findFirstByContentType(resp.Messages(), protobuf.ChatMessage_CONTACT_REQUEST) contactRequest := s.findFirstByContentType(resp.Messages(), protobuf.ChatMessage_CONTACT_REQUEST)
s.Require().NotNil(contactRequest) s.Require().NotNil(contactRequest)
mutualStateUpdate := s.findFirstByContentType(resp.Messages(), protobuf.ChatMessage_SYSTEM_MESSAGE_MUTUAL_STATE_UPDATE)
s.Require().NotNil(mutualStateUpdate)
s.Require().Equal(common.ContactRequestStatePending, contactRequest.ContactRequestState) s.Require().Equal(common.ContactRequestStatePending, contactRequest.ContactRequestState)
s.Require().Equal(messageText, contactRequest.Text) s.Require().Equal(messageText, contactRequest.Text)
mutualStateUpdate := s.findFirstByContentType(resp.Messages(), protobuf.ChatMessage_SYSTEM_MESSAGE_MUTUAL_EVENT_SENT)
s.Require().NotNil(mutualStateUpdate)
s.Require().Equal(mutualStateUpdate.From, contactRequest.From) s.Require().Equal(mutualStateUpdate.From, contactRequest.From)
s.Require().Equal(mutualStateUpdate.ChatId, contactRequest.From) s.Require().Equal(mutualStateUpdate.ChatId, contactRequest.From)
s.Require().Equal(mutualStateUpdate.Text, "@"+contactRequest.From+" sent you a contact request") s.Require().Equal(mutualStateUpdate.Text, fmt.Sprintf(incomingMutualStateEventSentDefaultText, contactRequest.From))
// Check activity center notification is of the right type // Check activity center notification is of the right type
s.Require().Len(resp.ActivityCenterNotifications(), 1) s.Require().Len(resp.ActivityCenterNotifications(), 1)
@ -200,15 +196,15 @@ func (s *MessengerContactRequestSuite) acceptContactRequest(contactRequest *comm
contactRequestMsg := s.findFirstByContentType(resp.Messages(), protobuf.ChatMessage_CONTACT_REQUEST) contactRequestMsg := s.findFirstByContentType(resp.Messages(), protobuf.ChatMessage_CONTACT_REQUEST)
s.Require().NotNil(contactRequestMsg) s.Require().NotNil(contactRequestMsg)
mutualStateUpdate := s.findFirstByContentType(resp.Messages(), protobuf.ChatMessage_SYSTEM_MESSAGE_MUTUAL_STATE_UPDATE) mutualStateUpdate := s.findFirstByContentType(resp.Messages(), protobuf.ChatMessage_SYSTEM_MESSAGE_MUTUAL_EVENT_ACCEPTED)
s.Require().NotNil(mutualStateUpdate) s.Require().NotNil(mutualStateUpdate)
s.Require().Equal(contactRequestMsg.ID, contactRequest.ID) s.Require().Equal(contactRequestMsg.ID, contactRequest.ID)
s.Require().Equal(common.ContactRequestStateAccepted, contactRequestMsg.ContactRequestState) s.Require().Equal(common.ContactRequestStateAccepted, contactRequestMsg.ContactRequestState)
// Reversed for add contact update
s.Require().Equal(mutualStateUpdate.From, contactRequestMsg.ChatId)
s.Require().Equal(mutualStateUpdate.ChatId, contactRequestMsg.From) s.Require().Equal(mutualStateUpdate.ChatId, contactRequestMsg.From)
s.Require().Equal(mutualStateUpdate.Text, "You added @"+contactRequestMsg.From+" as a contact") s.Require().Equal(mutualStateUpdate.From, contactRequestMsg.ChatId)
s.Require().Equal(mutualStateUpdate.Text, fmt.Sprintf(outgoingMutualStateEventAcceptedDefaultText, contactRequestMsg.From))
s.Require().Len(resp.ActivityCenterNotifications(), 1) s.Require().Len(resp.ActivityCenterNotifications(), 1)
s.Require().Equal(resp.ActivityCenterNotifications()[0].ID.String(), contactRequest.ID) s.Require().Equal(resp.ActivityCenterNotifications()[0].ID.String(), contactRequest.ID)
@ -260,7 +256,7 @@ func (s *MessengerContactRequestSuite) acceptContactRequest(contactRequest *comm
contactRequestMsg = s.findFirstByContentType(resp.Messages(), protobuf.ChatMessage_CONTACT_REQUEST) contactRequestMsg = s.findFirstByContentType(resp.Messages(), protobuf.ChatMessage_CONTACT_REQUEST)
s.Require().NotNil(contactRequestMsg) s.Require().NotNil(contactRequestMsg)
mutualStateUpdate = s.findFirstByContentType(resp.Messages(), protobuf.ChatMessage_SYSTEM_MESSAGE_MUTUAL_STATE_UPDATE) mutualStateUpdate = s.findFirstByContentType(resp.Messages(), protobuf.ChatMessage_SYSTEM_MESSAGE_MUTUAL_EVENT_ACCEPTED)
s.Require().NotNil(mutualStateUpdate) s.Require().NotNil(mutualStateUpdate)
s.Require().Equal(contactRequest.ID, contactRequestMsg.ID) s.Require().Equal(contactRequest.ID, contactRequestMsg.ID)
@ -269,7 +265,7 @@ func (s *MessengerContactRequestSuite) acceptContactRequest(contactRequest *comm
s.Require().Equal(mutualStateUpdate.From, contactRequestMsg.ChatId) s.Require().Equal(mutualStateUpdate.From, contactRequestMsg.ChatId)
s.Require().Equal(mutualStateUpdate.ChatId, contactRequestMsg.ChatId) s.Require().Equal(mutualStateUpdate.ChatId, contactRequestMsg.ChatId)
s.Require().Equal(mutualStateUpdate.Text, "@"+mutualStateUpdate.From+" added you as a contact") s.Require().Equal(mutualStateUpdate.Text, fmt.Sprintf(incomingMutualStateEventAcceptedDefaultText, contactRequestMsg.ChatId))
// Make sure we consider them a mutual contact, sender side // Make sure we consider them a mutual contact, sender side
mutualContacts = s.m.MutualContacts() mutualContacts = s.m.MutualContacts()
@ -474,13 +470,8 @@ func (s *MessengerContactRequestSuite) TestReceiveAndAcceptContactRequestTwice()
contactRequest = s.findFirstByContentType(resp.Messages(), protobuf.ChatMessage_CONTACT_REQUEST) contactRequest = s.findFirstByContentType(resp.Messages(), protobuf.ChatMessage_CONTACT_REQUEST)
s.Require().NotNil(contactRequest) s.Require().NotNil(contactRequest)
mutualStateUpdate := s.findFirstByContentType(resp.Messages(), protobuf.ChatMessage_SYSTEM_MESSAGE_MUTUAL_STATE_UPDATE)
s.Require().NotNil(mutualStateUpdate)
s.Require().Equal(common.ContactRequestStateAccepted, contactRequest.ContactRequestState) s.Require().Equal(common.ContactRequestStateAccepted, contactRequest.ContactRequestState)
s.Require().Equal(request.Message, contactRequest.Text) s.Require().Equal(request.Message, contactRequest.Text)
s.Require().Equal(mutualStateUpdate.From, s.m.myHexIdentity())
s.Require().Equal(mutualStateUpdate.ChatId, request.ID)
// Wait for the message to reach its destination // Wait for the message to reach its destination
resp, err = WaitOnMessengerResponse( resp, err = WaitOnMessengerResponse(
@ -536,7 +527,7 @@ func (s *MessengerContactRequestSuite) TestAcceptLatestContactRequestForContact(
contactRequestMsg := s.findFirstByContentType(resp.Messages(), protobuf.ChatMessage_CONTACT_REQUEST) contactRequestMsg := s.findFirstByContentType(resp.Messages(), protobuf.ChatMessage_CONTACT_REQUEST)
s.Require().NotNil(contactRequestMsg) s.Require().NotNil(contactRequestMsg)
mutualStateUpdate := s.findFirstByContentType(resp.Messages(), protobuf.ChatMessage_SYSTEM_MESSAGE_MUTUAL_STATE_UPDATE) mutualStateUpdate := s.findFirstByContentType(resp.Messages(), protobuf.ChatMessage_SYSTEM_MESSAGE_MUTUAL_EVENT_ACCEPTED)
s.Require().NotNil(mutualStateUpdate) s.Require().NotNil(mutualStateUpdate)
s.Require().Equal(contactRequestMsg.ID, contactRequest.ID) s.Require().Equal(contactRequestMsg.ID, contactRequest.ID)
@ -580,7 +571,7 @@ func (s *MessengerContactRequestSuite) TestAcceptLatestContactRequestForContact(
contactRequestMsg = s.findFirstByContentType(resp.Messages(), protobuf.ChatMessage_CONTACT_REQUEST) contactRequestMsg = s.findFirstByContentType(resp.Messages(), protobuf.ChatMessage_CONTACT_REQUEST)
s.Require().NotNil(contactRequestMsg) s.Require().NotNil(contactRequestMsg)
mutualStateUpdate = s.findFirstByContentType(resp.Messages(), protobuf.ChatMessage_SYSTEM_MESSAGE_MUTUAL_STATE_UPDATE) mutualStateUpdate = s.findFirstByContentType(resp.Messages(), protobuf.ChatMessage_SYSTEM_MESSAGE_MUTUAL_EVENT_ACCEPTED)
s.Require().NotNil(mutualStateUpdate) s.Require().NotNil(mutualStateUpdate)
s.Require().Equal(common.ContactRequestStateAccepted, contactRequestMsg.ContactRequestState) s.Require().Equal(common.ContactRequestStateAccepted, contactRequestMsg.ContactRequestState)
@ -1068,15 +1059,9 @@ func (s *MessengerContactRequestSuite) TestBobSendsContactRequestAfterDecliningO
contactRequest = s.findFirstByContentType(resp.Messages(), protobuf.ChatMessage_CONTACT_REQUEST) contactRequest = s.findFirstByContentType(resp.Messages(), protobuf.ChatMessage_CONTACT_REQUEST)
s.Require().NotNil(contactRequest) s.Require().NotNil(contactRequest)
mutualStateUpdate := s.findFirstByContentType(resp.Messages(), protobuf.ChatMessage_SYSTEM_MESSAGE_MUTUAL_STATE_UPDATE)
s.Require().NotNil(mutualStateUpdate)
s.Require().Equal(common.ContactRequestStateAccepted, contactRequest.ContactRequestState) s.Require().Equal(common.ContactRequestStateAccepted, contactRequest.ContactRequestState)
s.Require().Equal(requestFromBob.Message, contactRequest.Text) s.Require().Equal(requestFromBob.Message, contactRequest.Text)
s.Require().Equal(mutualStateUpdate.From, contactRequest.From)
s.Require().Equal(mutualStateUpdate.ChatId, contactRequest.ChatId)
// Check pending notification // Check pending notification
s.Require().Len(resp.ActivityCenterNotifications(), 1) s.Require().Len(resp.ActivityCenterNotifications(), 1)
s.Require().Equal(ActivityCenterNotificationTypeContactRequest, resp.ActivityCenterNotifications()[0].Type) s.Require().Equal(ActivityCenterNotificationTypeContactRequest, resp.ActivityCenterNotifications()[0].Type)
@ -1141,7 +1126,10 @@ func (s *MessengerContactRequestSuite) TestBobSendsContactRequestAfterDecliningO
} }
func (s *MessengerContactRequestSuite) TestBuildContact() { func (s *MessengerContactRequestSuite) TestBuildContact() {
contactID := types.EncodeHex(crypto.FromECDSAPub(&s.m.identity.PublicKey)) contactKey, err := crypto.GenerateKey()
s.Require().NoError(err)
contactID := types.EncodeHex(crypto.FromECDSAPub(&contactKey.PublicKey))
contact, err := s.m.BuildContact(&requests.BuildContact{PublicKey: contactID}) contact, err := s.m.BuildContact(&requests.BuildContact{PublicKey: contactID})
s.Require().NoError(err) s.Require().NoError(err)

View File

@ -18,21 +18,32 @@ import (
"github.com/status-im/status-go/protocol/transport" "github.com/status-im/status-go/protocol/transport"
) )
const outgoingMutualStateEventSentDefaultText = "You sent a contact request to @%s"
const outgoingMutualStateEventAcceptedDefaultText = "You accepted @%s's contact request"
const outgoingMutualStateEventRemovedDefaultText = "You removed @%s as a contact"
const incomingMutualStateEventSentDefaultText = "@%s sent you a contact request"
const incomingMutualStateEventAcceptedDefaultText = "@%s accepted your contact request"
const incomingMutualStateEventRemovedDefaultText = "@%s removed you as a contact"
func (m *Messenger) prepareMutualStateUpdateMessage(contactID string, updateType MutualStateUpdateType, clock uint64, timestamp uint64, outgoing bool) (*common.Message, error) { func (m *Messenger) prepareMutualStateUpdateMessage(contactID string, updateType MutualStateUpdateType, clock uint64, timestamp uint64, outgoing bool) (*common.Message, error) {
var text string var text string
var to string var to string
var from string var from string
var contentType protobuf.ChatMessage_ContentType
if outgoing { if outgoing {
to = contactID to = contactID
from = m.myHexIdentity() from = m.myHexIdentity()
switch updateType { switch updateType {
case MutualStateUpdateTypeSent: case MutualStateUpdateTypeSent:
text = "You sent a contact request to @" + to text = fmt.Sprintf(outgoingMutualStateEventSentDefaultText, contactID)
contentType = protobuf.ChatMessage_SYSTEM_MESSAGE_MUTUAL_EVENT_SENT
case MutualStateUpdateTypeAdded: case MutualStateUpdateTypeAdded:
text = "You added @" + to + " as a contact" text = fmt.Sprintf(outgoingMutualStateEventAcceptedDefaultText, contactID)
contentType = protobuf.ChatMessage_SYSTEM_MESSAGE_MUTUAL_EVENT_ACCEPTED
case MutualStateUpdateTypeRemoved: case MutualStateUpdateTypeRemoved:
text = "You removed @" + to + " as a contact" text = fmt.Sprintf(outgoingMutualStateEventRemovedDefaultText, contactID)
contentType = protobuf.ChatMessage_SYSTEM_MESSAGE_MUTUAL_EVENT_REMOVED
default: default:
return nil, fmt.Errorf("unhandled outgoing MutualStateUpdateType = %d", updateType) return nil, fmt.Errorf("unhandled outgoing MutualStateUpdateType = %d", updateType)
} }
@ -42,11 +53,14 @@ func (m *Messenger) prepareMutualStateUpdateMessage(contactID string, updateType
switch updateType { switch updateType {
case MutualStateUpdateTypeSent: case MutualStateUpdateTypeSent:
text = "@" + from + " sent you a contact request" text = fmt.Sprintf(incomingMutualStateEventSentDefaultText, contactID)
contentType = protobuf.ChatMessage_SYSTEM_MESSAGE_MUTUAL_EVENT_SENT
case MutualStateUpdateTypeAdded: case MutualStateUpdateTypeAdded:
text = "@" + from + " added you as a contact" text = fmt.Sprintf(incomingMutualStateEventAcceptedDefaultText, contactID)
contentType = protobuf.ChatMessage_SYSTEM_MESSAGE_MUTUAL_EVENT_ACCEPTED
case MutualStateUpdateTypeRemoved: case MutualStateUpdateTypeRemoved:
text = "@" + from + " removed you as a contact" text = fmt.Sprintf(incomingMutualStateEventRemovedDefaultText, contactID)
contentType = protobuf.ChatMessage_SYSTEM_MESSAGE_MUTUAL_EVENT_REMOVED
default: default:
return nil, fmt.Errorf("unhandled incoming MutualStateUpdateType = %d", updateType) return nil, fmt.Errorf("unhandled incoming MutualStateUpdateType = %d", updateType)
} }
@ -57,7 +71,7 @@ func (m *Messenger) prepareMutualStateUpdateMessage(contactID string, updateType
ChatId: contactID, ChatId: contactID,
Text: text, Text: text,
MessageType: protobuf.MessageType_ONE_TO_ONE, MessageType: protobuf.MessageType_ONE_TO_ONE,
ContentType: protobuf.ChatMessage_SYSTEM_MESSAGE_MUTUAL_STATE_UPDATE, ContentType: contentType,
Clock: clock, Clock: clock,
Timestamp: timestamp, Timestamp: timestamp,
}, },
@ -71,27 +85,6 @@ func (m *Messenger) prepareMutualStateUpdateMessage(contactID string, updateType
return message, nil return message, nil
} }
func (m *Messenger) removeLastMutualStateUpdateMessage(response *MessengerResponse, contactID string) error {
messages, _, err := m.MessageByChatID(contactID, "", 5)
if err != nil {
return err
}
for _, message := range messages {
if message.ContentType == protobuf.ChatMessage_SYSTEM_MESSAGE_MUTUAL_STATE_UPDATE {
message.Deleted = true
message.DeletedBy = m.myHexIdentity()
err = m.persistence.SaveMessages([]*common.Message{message})
if err != nil {
return err
}
response.AddMessage(message)
response.AddRemovedMessage(&RemovedMessage{MessageID: message.ID, ChatID: contactID, DeletedBy: m.myHexIdentity()})
}
}
return nil
}
func (m *Messenger) acceptContactRequest(ctx context.Context, requestID string, syncing bool) (*MessengerResponse, error) { func (m *Messenger) acceptContactRequest(ctx context.Context, requestID string, syncing bool) (*MessengerResponse, error) {
contactRequest, err := m.persistence.MessageByID(requestID) contactRequest, err := m.persistence.MessageByID(requestID)
if err != nil { if err != nil {
@ -123,26 +116,6 @@ func (m *Messenger) acceptContactRequest(ctx context.Context, requestID string,
} }
response.AddChat(chat) response.AddChat(chat)
// Remove last sent system message
err = m.removeLastMutualStateUpdateMessage(response, contactRequest.From)
if err != nil {
return nil, err
}
// System message for mutual state update
clock, timestamp := chat.NextClockAndTimestamp(m.getTimesource())
updateMessage, err := m.prepareMutualStateUpdateMessage(contactRequest.From, MutualStateUpdateTypeAdded, clock, timestamp, true)
if err != nil {
return nil, err
}
m.prepareMessage(updateMessage, m.httpServer)
err = m.persistence.SaveMessages([]*common.Message{updateMessage})
if err != nil {
return nil, err
}
response.AddMessage(updateMessage)
return response, nil return response, nil
} }
@ -289,11 +262,17 @@ func (m *Messenger) updateAcceptedContactRequest(response *MessengerResponse, co
return nil, errors.New("failed to update contact request: contact not found") return nil, errors.New("failed to update contact request: contact not found")
} }
_, clock, err := m.getOneToOneAndNextClock(contact) chat, ok := m.allChats.Load(contact.ID)
if !ok {
return nil, errors.New("no chat found for accepted contact request")
}
notification, err := m.persistence.GetActivityCenterNotificationByID(types.FromHex(contactRequest.ID))
if err != nil { if err != nil {
return nil, err return nil, err
} }
clock, _ := chat.NextClockAndTimestamp(m.transport)
contact.AcceptContactRequest(clock) contact.AcceptContactRequest(clock)
acceptContactRequest := &protobuf.AcceptContactRequest{ acceptContactRequest := &protobuf.AcceptContactRequest{
@ -315,11 +294,6 @@ func (m *Messenger) updateAcceptedContactRequest(response *MessengerResponse, co
return nil, err return nil, err
} }
notification, err := m.persistence.GetActivityCenterNotificationByID(types.FromHex(contactRequest.ID))
if err != nil {
return nil, err
}
if response == nil { if response == nil {
response = &MessengerResponse{} response = &MessengerResponse{}
} }
@ -341,19 +315,9 @@ func (m *Messenger) updateAcceptedContactRequest(response *MessengerResponse, co
response.AddMessage(contactRequest) response.AddMessage(contactRequest)
response.AddContact(contact) response.AddContact(contact)
// Remove last sent system message // Add mutual state update message for incoming contact request
err = m.removeLastMutualStateUpdateMessage(response, contact.ID) clock, timestamp := chat.NextClockAndTimestamp(m.transport)
if err != nil { updateMessage, err := m.prepareMutualStateUpdateMessage(contact.ID, MutualStateUpdateTypeAdded, clock, timestamp, true)
return nil, err
}
// System message for mutual state update
chat, clock, err := m.getOneToOneAndNextClock(contact)
if err != nil {
return nil, err
}
timestamp := m.getTimesource().GetCurrentTime()
updateMessage, err := m.prepareMutualStateUpdateMessage(contact.ID, MutualStateUpdateTypeAdded, clock, timestamp, false)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -375,7 +339,9 @@ func (m *Messenger) addContact(ctx context.Context, pubKey, ensName, nickname, d
return nil, err return nil, err
} }
_, clock, err := m.getOneToOneAndNextClock(contact) response := &MessengerResponse{}
chat, clock, err := m.getOneToOneAndNextClock(contact)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -464,8 +430,6 @@ func (m *Messenger) addContact(ctx context.Context, pubKey, ensName, nickname, d
return nil, err return nil, err
} }
response := &MessengerResponse{}
if sendContactUpdate { if sendContactUpdate {
response, err = m.sendContactUpdate(context.Background(), pubKey, displayName, ensName, "", m.dispatchMessage) response, err = m.sendContactUpdate(context.Background(), pubKey, displayName, ensName, "", m.dispatchMessage)
if err != nil { if err != nil {
@ -484,16 +448,6 @@ func (m *Messenger) addContact(ctx context.Context, pubKey, ensName, nickname, d
} }
} }
// Send profile picture with contact request
chat, ok := m.allChats.Load(contact.ID)
if !ok {
chat = OneToOneFromPublicKey(publicKey, m.getTimesource())
chat.Active = false
if err := m.saveChat(chat); err != nil {
return nil, err
}
}
// Sends a standalone ChatIdentity message // Sends a standalone ChatIdentity message
err = m.handleStandaloneChatIdentity(chat) err = m.handleStandaloneChatIdentity(chat)
if err != nil { if err != nil {
@ -514,6 +468,23 @@ func (m *Messenger) addContact(ctx context.Context, pubKey, ensName, nickname, d
return nil, err return nil, err
} }
// Add mutual state update message for outgoing contact request
if len(contactRequestID) == 0 {
clock, timestamp := chat.NextClockAndTimestamp(m.transport)
updateMessage, err := m.prepareMutualStateUpdateMessage(contact.ID, MutualStateUpdateTypeSent, clock, timestamp, true)
if err != nil {
return nil, err
}
m.prepareMessage(updateMessage, m.httpServer)
err = m.persistence.SaveMessages([]*common.Message{updateMessage})
if err != nil {
return nil, err
}
response.AddMessage(updateMessage)
response.AddChat(chat)
}
// Add outgoing contact request notification // Add outgoing contact request notification
if createOutgoingContactRequestNotification { if createOutgoingContactRequestNotification {
clock, timestamp := chat.NextClockAndTimestamp(m.transport) clock, timestamp := chat.NextClockAndTimestamp(m.transport)
@ -533,21 +504,6 @@ func (m *Messenger) addContact(ctx context.Context, pubKey, ensName, nickname, d
return nil, err return nil, err
} }
// System message for mutual state update
clock, timestamp = chat.NextClockAndTimestamp(m.transport)
updateMessage, err := m.prepareMutualStateUpdateMessage(contact.ID, MutualStateUpdateTypeSent, clock, timestamp, true)
if err != nil {
return nil, err
}
m.prepareMessage(updateMessage, m.httpServer)
err = m.persistence.SaveMessages([]*common.Message{updateMessage})
if err != nil {
return nil, err
}
response.AddMessage(updateMessage)
response.AddChat(chat)
notification := m.generateOutgoingContactRequestNotification(contact, contactRequest) notification := m.generateOutgoingContactRequestNotification(contact, contactRequest)
err = m.addActivityCenterNotification(response, notification) err = m.addActivityCenterNotification(response, notification)
if err != nil { if err != nil {
@ -663,11 +619,6 @@ func (m *Messenger) removeContact(ctx context.Context, response *MessengerRespon
response.AddChat(chat) response.AddChat(chat)
// Next we retract a contact request // Next we retract a contact request
_, clock, err = m.getOneToOneAndNextClock(contact)
if err != nil {
return err
}
contact.RetractContactRequest(clock) contact.RetractContactRequest(clock)
contact.LastUpdatedLocally = m.getTimesource().GetCurrentTime() contact.LastUpdatedLocally = m.getTimesource().GetCurrentTime()

View File

@ -354,36 +354,6 @@ func (m *Messenger) createIncomingContactRequestNotification(contact *Contact, m
return m.addActivityCenterNotification(messageState.Response, notification) return m.addActivityCenterNotification(messageState.Response, notification)
} }
func (m *Messenger) createIncomingContactRequestEventAndNotification(contact *Contact, messageState *ReceivedMessageState, contactRequest *common.Message, createNewNotification bool) error {
var updateType MutualStateUpdateType
if contactRequest.ContactRequestState == common.ContactRequestStateAccepted {
updateType = MutualStateUpdateTypeAdded
} else {
updateType = MutualStateUpdateTypeSent
}
// System message for mutual state update
chat, clock, err := m.getOneToOneAndNextClock(contact)
if err != nil {
return err
}
timestamp := m.getTimesource().GetCurrentTime()
updateMessage, err := m.prepareMutualStateUpdateMessage(contact.ID, updateType, clock, timestamp, false)
if err != nil {
return err
}
m.prepareMessage(updateMessage, m.httpServer)
err = m.persistence.SaveMessages([]*common.Message{updateMessage})
if err != nil {
return err
}
messageState.Response.AddMessage(updateMessage)
messageState.Response.AddChat(chat)
return m.createIncomingContactRequestNotification(contact, messageState, contactRequest, createNewNotification)
}
func (m *Messenger) handleCommandMessage(state *ReceivedMessageState, message *common.Message) error { func (m *Messenger) handleCommandMessage(state *ReceivedMessageState, message *common.Message) error {
message.ID = state.CurrentMessageState.MessageID message.ID = state.CurrentMessageState.MessageID
message.From = state.CurrentMessageState.Contact.ID message.From = state.CurrentMessageState.Contact.ID
@ -492,7 +462,7 @@ func (m *Messenger) syncContactRequestForInstallationContact(contact *Contact, s
return err return err
} }
} else { } else {
err = m.createIncomingContactRequestEventAndNotification(contact, state, contactRequest, true) err = m.createIncomingContactRequestNotification(contact, state, contactRequest, true)
if err != nil { if err != nil {
return err return err
} }
@ -953,6 +923,21 @@ func (m *Messenger) handleAcceptContactRequestMessage(state *ReceivedMessageStat
chat.Active = true chat.Active = true
} }
// Add mutual state update message for incoming contact request
clock, timestamp := chat.NextClockAndTimestamp(m.transport)
updateMessage, err := m.prepareMutualStateUpdateMessage(contact.ID, MutualStateUpdateTypeAdded, clock, timestamp, false)
if err != nil {
return err
}
m.prepareMessage(updateMessage, m.httpServer)
err = m.persistence.SaveMessages([]*common.Message{updateMessage})
if err != nil {
return err
}
state.Response.AddMessage(updateMessage)
state.Response.AddChat(chat) state.Response.AddChat(chat)
state.AllChats.Store(chat.ID, chat) state.AllChats.Store(chat.ID, chat)
} }
@ -965,7 +950,7 @@ func (m *Messenger) handleAcceptContactRequestMessage(state *ReceivedMessageStat
return err return err
} }
} else { } else {
err = m.createIncomingContactRequestEventAndNotification(contact, state, request, processingResponse.newContactRequestReceived) err = m.createIncomingContactRequestNotification(contact, state, request, processingResponse.newContactRequestReceived)
if err != nil { if err != nil {
return err return err
} }
@ -1100,7 +1085,7 @@ func (m *Messenger) HandleContactUpdate(state *ReceivedMessageState, message pro
return err return err
} }
err = m.createIncomingContactRequestEventAndNotification(contact, state, contactRequest, true) err = m.createIncomingContactRequestNotification(contact, state, contactRequest, true)
if err != nil { if err != nil {
return err return err
} }
@ -1109,7 +1094,6 @@ func (m *Messenger) HandleContactUpdate(state *ReceivedMessageState, message pro
logger.Debug("handled propagated state", zap.Any("state after update", contact.ContactRequestPropagatedState())) logger.Debug("handled propagated state", zap.Any("state after update", contact.ContactRequestPropagatedState()))
state.ModifiedContacts.Store(contact.ID, true) state.ModifiedContacts.Store(contact.ID, true)
state.AllContacts.Store(contact.ID, contact) state.AllContacts.Store(contact.ID, contact)
} }
if contact.LastUpdated < message.Clock { if contact.LastUpdated < message.Clock {
@ -1124,7 +1108,7 @@ func (m *Messenger) HandleContactUpdate(state *ReceivedMessageState, message pro
r := contact.ContactRequestReceived(message.ContactRequestClock) r := contact.ContactRequestReceived(message.ContactRequestClock)
if r.newContactRequestReceived { if r.newContactRequestReceived {
err = m.createIncomingContactRequestEventAndNotification(contact, state, nil, true) err = m.createIncomingContactRequestNotification(contact, state, nil, true)
if err != nil { if err != nil {
return err return err
} }
@ -2098,7 +2082,21 @@ func (m *Messenger) handleChatMessage(state *ReceivedMessageState, forceSeen boo
receivedMessage.ContactRequestState = common.ContactRequestStatePending receivedMessage.ContactRequestState = common.ContactRequestStatePending
} }
err = m.createIncomingContactRequestEventAndNotification(contact, state, receivedMessage, true) // Add mutual state update message for outgoing contact request
clock := receivedMessage.Clock - 1
updateMessage, err := m.prepareMutualStateUpdateMessage(contact.ID, MutualStateUpdateTypeSent, clock, receivedMessage.Timestamp, false)
if err != nil {
return err
}
m.prepareMessage(updateMessage, m.httpServer)
err = m.persistence.SaveMessages([]*common.Message{updateMessage})
if err != nil {
return err
}
state.Response.AddMessage(updateMessage)
err = m.createIncomingContactRequestNotification(contact, state, receivedMessage, true)
if err != nil { if err != nil {
return err return err
} }
@ -2125,7 +2123,7 @@ func (m *Messenger) handleChatMessage(state *ReceivedMessageState, forceSeen boo
state.AllContacts.Store(chatContact.ID, chatContact) state.AllContacts.Store(chatContact.ID, chatContact)
if sendNotification { if sendNotification {
err = m.createIncomingContactRequestEventAndNotification(chatContact, state, receivedMessage, true) err = m.createIncomingContactRequestNotification(chatContact, state, receivedMessage, true)
if err != nil { if err != nil {
return err return err
} }
@ -2168,7 +2166,6 @@ func (m *Messenger) handleChatMessage(state *ReceivedMessageState, forceSeen boo
return err return err
} }
} }
// Set in the modified maps chat // Set in the modified maps chat
state.Response.AddChat(chat) state.Response.AddChat(chat)
// TODO(samyoul) remove storing of an updated reference pointer? // TODO(samyoul) remove storing of an updated reference pointer?

View File

@ -69,7 +69,12 @@ const (
ChatMessage_IDENTITY_VERIFICATION ChatMessage_ContentType = 13 ChatMessage_IDENTITY_VERIFICATION ChatMessage_ContentType = 13
// Only local // Only local
ChatMessage_SYSTEM_MESSAGE_PINNED_MESSAGE ChatMessage_ContentType = 14 ChatMessage_SYSTEM_MESSAGE_PINNED_MESSAGE ChatMessage_ContentType = 14
ChatMessage_SYSTEM_MESSAGE_MUTUAL_STATE_UPDATE ChatMessage_ContentType = 15 // Only local
ChatMessage_SYSTEM_MESSAGE_MUTUAL_EVENT_SENT ChatMessage_ContentType = 15
// Only local
ChatMessage_SYSTEM_MESSAGE_MUTUAL_EVENT_ACCEPTED ChatMessage_ContentType = 16
// Only local
ChatMessage_SYSTEM_MESSAGE_MUTUAL_EVENT_REMOVED ChatMessage_ContentType = 17
) )
var ChatMessage_ContentType_name = map[int32]string{ var ChatMessage_ContentType_name = map[int32]string{
@ -88,7 +93,9 @@ var ChatMessage_ContentType_name = map[int32]string{
12: "DISCORD_MESSAGE", 12: "DISCORD_MESSAGE",
13: "IDENTITY_VERIFICATION", 13: "IDENTITY_VERIFICATION",
14: "SYSTEM_MESSAGE_PINNED_MESSAGE", 14: "SYSTEM_MESSAGE_PINNED_MESSAGE",
15: "SYSTEM_MESSAGE_MUTUAL_STATE_UPDATE", 15: "SYSTEM_MESSAGE_MUTUAL_EVENT_SENT",
16: "SYSTEM_MESSAGE_MUTUAL_EVENT_ACCEPTED",
17: "SYSTEM_MESSAGE_MUTUAL_EVENT_REMOVED",
} }
var ChatMessage_ContentType_value = map[string]int32{ var ChatMessage_ContentType_value = map[string]int32{
@ -107,7 +114,9 @@ var ChatMessage_ContentType_value = map[string]int32{
"DISCORD_MESSAGE": 12, "DISCORD_MESSAGE": 12,
"IDENTITY_VERIFICATION": 13, "IDENTITY_VERIFICATION": 13,
"SYSTEM_MESSAGE_PINNED_MESSAGE": 14, "SYSTEM_MESSAGE_PINNED_MESSAGE": 14,
"SYSTEM_MESSAGE_MUTUAL_STATE_UPDATE": 15, "SYSTEM_MESSAGE_MUTUAL_EVENT_SENT": 15,
"SYSTEM_MESSAGE_MUTUAL_EVENT_ACCEPTED": 16,
"SYSTEM_MESSAGE_MUTUAL_EVENT_REMOVED": 17,
} }
func (x ChatMessage_ContentType) String() string { func (x ChatMessage_ContentType) String() string {
@ -1191,95 +1200,96 @@ func init() {
} }
var fileDescriptor_263952f55fd35689 = []byte{ var fileDescriptor_263952f55fd35689 = []byte{
// 1430 bytes of a gzipped FileDescriptorProto // 1454 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x56, 0x5f, 0x73, 0xdb, 0x44, 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x56, 0xcd, 0x6e, 0xdb, 0xc6,
0x10, 0x8f, 0x1d, 0xff, 0x89, 0x56, 0xb6, 0x23, 0xae, 0x69, 0xab, 0x76, 0x9a, 0x36, 0xd5, 0x74, 0x13, 0xb7, 0xbe, 0xcd, 0xd1, 0x87, 0x99, 0x8d, 0x93, 0x30, 0x41, 0x9c, 0x28, 0xfa, 0x07, 0x88,
0x68, 0x18, 0x98, 0x30, 0x53, 0x0a, 0xd3, 0x19, 0x86, 0x61, 0x14, 0x5b, 0x4d, 0x44, 0x6b, 0xc7, 0xff, 0x68, 0xe1, 0x02, 0x69, 0x5a, 0x04, 0x28, 0x8a, 0x82, 0x96, 0x18, 0x9b, 0x4d, 0xf4, 0xd1,
0x9c, 0xe5, 0x96, 0xf0, 0xa2, 0x51, 0xa4, 0x4b, 0xac, 0x89, 0x2c, 0x19, 0xe9, 0x04, 0x98, 0x77, 0x15, 0xe5, 0xd4, 0xbd, 0x10, 0x34, 0xb9, 0xb6, 0x08, 0x53, 0xa4, 0x4a, 0xae, 0xda, 0xaa, 0xf7,
0x66, 0xf8, 0x44, 0x7c, 0x06, 0x1e, 0x78, 0xe5, 0x81, 0x2f, 0xc0, 0x0b, 0xaf, 0x7c, 0x00, 0xe6, 0xbe, 0x52, 0x4f, 0x7d, 0x80, 0x1e, 0x7a, 0xed, 0x21, 0x2f, 0xd0, 0x27, 0xe8, 0x03, 0x14, 0xbb,
0xee, 0xf4, 0xcf, 0xa6, 0x49, 0x99, 0x3e, 0xe9, 0x76, 0xb5, 0xbb, 0xb7, 0xfb, 0xdb, 0xbd, 0xdd, 0xcb, 0x4f, 0xd5, 0x76, 0x8a, 0x9c, 0xb8, 0x33, 0x9c, 0x99, 0x9d, 0xf9, 0xcd, 0xec, 0xcc, 0x00,
0x05, 0xe4, 0xce, 0x1c, 0x6a, 0xcf, 0x49, 0x92, 0x38, 0x17, 0xe4, 0x60, 0x11, 0x47, 0x34, 0x42, 0xb2, 0xe7, 0x16, 0x35, 0x17, 0x24, 0x8a, 0xac, 0x0b, 0x72, 0xb0, 0x0c, 0x03, 0x1a, 0xa0, 0x6d,
0x5b, 0xfc, 0x73, 0x96, 0x9e, 0xdf, 0x95, 0x49, 0x98, 0xce, 0x13, 0xc1, 0xbe, 0xdb, 0x75, 0xa3, 0xfe, 0x39, 0x5b, 0x9d, 0x3f, 0x68, 0x12, 0x7f, 0xb5, 0x88, 0x04, 0xfb, 0x41, 0xdb, 0x0e, 0x7c,
0x90, 0x3a, 0x2e, 0x15, 0xa4, 0xf6, 0x0c, 0x7a, 0x13, 0xea, 0xbb, 0x97, 0x24, 0x1e, 0x0a, 0x6d, 0x6a, 0xd9, 0x54, 0x90, 0xbd, 0x97, 0xd0, 0x99, 0x52, 0xd7, 0xbe, 0x24, 0xe1, 0x50, 0x68, 0x23,
0x84, 0xa0, 0x31, 0x73, 0x92, 0x99, 0x5a, 0xdb, 0xab, 0xed, 0x4b, 0x98, 0x9f, 0x19, 0x6f, 0xe1, 0x04, 0xd5, 0xb9, 0x15, 0xcd, 0x95, 0x52, 0xb7, 0xb4, 0x2f, 0x61, 0x7e, 0x66, 0xbc, 0xa5, 0x65,
0xb8, 0x97, 0x6a, 0x7d, 0xaf, 0xb6, 0xdf, 0xc4, 0xfc, 0xac, 0xfd, 0x56, 0x83, 0x8e, 0x39, 0x77, 0x5f, 0x2a, 0xe5, 0x6e, 0x69, 0xbf, 0x86, 0xf9, 0xb9, 0xf7, 0x7b, 0x09, 0x5a, 0xfa, 0xc2, 0xba,
0x2e, 0x48, 0xae, 0xa8, 0x42, 0x7b, 0xe1, 0x2c, 0x83, 0xc8, 0xf1, 0xb8, 0x6e, 0x07, 0xe7, 0x24, 0x20, 0x89, 0xa2, 0x02, 0x8d, 0xa5, 0xb5, 0xf6, 0x02, 0xcb, 0xe1, 0xba, 0x2d, 0x9c, 0x90, 0xe8,
0x7a, 0x0c, 0x0d, 0xba, 0x5c, 0x10, 0xae, 0xde, 0x7b, 0x72, 0xe3, 0x20, 0xf7, 0xec, 0x80, 0xeb, 0x19, 0x54, 0xe9, 0x7a, 0x49, 0xb8, 0x7a, 0xe7, 0xf9, 0xed, 0x83, 0xc4, 0xb3, 0x03, 0xae, 0x6f,
0x5b, 0xcb, 0x05, 0xc1, 0x5c, 0x00, 0xdd, 0x81, 0x2d, 0x27, 0x38, 0x4b, 0xe7, 0xb6, 0xef, 0xa9, 0xac, 0x97, 0x04, 0x73, 0x01, 0x74, 0x1f, 0xb6, 0x2d, 0xef, 0x6c, 0xb5, 0x30, 0x5d, 0x47, 0xa9,
0x9b, 0xfc, 0xfe, 0x36, 0xa7, 0x4d, 0x0f, 0xed, 0x40, 0xf3, 0x07, 0xdf, 0xa3, 0x33, 0xb5, 0xb1, 0xf0, 0xfb, 0x1b, 0x9c, 0xd6, 0x1d, 0xb4, 0x0b, 0xb5, 0x1f, 0x5d, 0x87, 0xce, 0x95, 0x6a, 0xb7,
0x57, 0xdb, 0xef, 0x62, 0x41, 0xa0, 0x5b, 0xd0, 0x9a, 0x11, 0xff, 0x62, 0x46, 0xd5, 0x26, 0x67, 0xb4, 0xdf, 0xc6, 0x82, 0x40, 0x77, 0xa1, 0x3e, 0x27, 0xee, 0xc5, 0x9c, 0x2a, 0x35, 0xce, 0x8e,
0x67, 0x14, 0xfa, 0x08, 0x50, 0x66, 0x88, 0xdd, 0x90, 0xd8, 0x6e, 0x94, 0x86, 0x54, 0x6d, 0x71, 0x29, 0xf4, 0x31, 0xa0, 0xd8, 0x10, 0xbb, 0x21, 0x32, 0xed, 0x60, 0xe5, 0x53, 0xa5, 0xce, 0x65,
0x19, 0x45, 0x98, 0xe4, 0x3f, 0xfa, 0x8c, 0xaf, 0xfd, 0x5a, 0x83, 0x8e, 0x9e, 0x7a, 0x7e, 0xf4, 0x64, 0x61, 0x92, 0xff, 0xe8, 0x33, 0x7e, 0xef, 0xd7, 0x12, 0xb4, 0xd4, 0x95, 0xe3, 0x06, 0xef,
0xf6, 0x50, 0x9e, 0xae, 0x84, 0xb2, 0x57, 0x86, 0x52, 0xd5, 0x17, 0x44, 0x25, 0xae, 0x07, 0x20, 0x0f, 0xe5, 0x45, 0x21, 0x94, 0x6e, 0x16, 0x4a, 0x5e, 0x5f, 0x10, 0xb9, 0xb8, 0x1e, 0x43, 0xd3,
0x7b, 0x69, 0xec, 0x50, 0x3f, 0x0a, 0xed, 0x79, 0xc2, 0x43, 0x6b, 0x60, 0xc8, 0x59, 0xc3, 0x44, 0x59, 0x85, 0x16, 0x75, 0x03, 0xdf, 0x5c, 0x44, 0x3c, 0xb4, 0x2a, 0x86, 0x84, 0x35, 0x8c, 0x7a,
0xfb, 0x14, 0xa4, 0x42, 0x07, 0xdd, 0x02, 0x34, 0x1d, 0xbd, 0x18, 0x9d, 0xbc, 0x1e, 0xd9, 0xfa, 0x9f, 0x81, 0x94, 0xea, 0xa0, 0xbb, 0x80, 0x66, 0xa3, 0xd7, 0xa3, 0xf1, 0xdb, 0x91, 0xa9, 0xce,
0x74, 0x60, 0x9e, 0xd8, 0xd6, 0xe9, 0xd8, 0x50, 0x36, 0x50, 0x1b, 0x36, 0x75, 0xbd, 0xaf, 0xd4, 0x06, 0xfa, 0xd8, 0x34, 0x4e, 0x27, 0x9a, 0xbc, 0x85, 0x1a, 0x50, 0x51, 0xd5, 0xbe, 0x5c, 0xe2,
0xf8, 0x61, 0x88, 0x95, 0xba, 0xf6, 0x73, 0x1d, 0x64, 0xc3, 0xf3, 0x69, 0xee, 0xf7, 0x0e, 0x34, 0x87, 0x21, 0x96, 0xcb, 0xbd, 0x5f, 0xca, 0xd0, 0xd4, 0x1c, 0x97, 0x26, 0x7e, 0xef, 0x42, 0xcd,
0xdd, 0x20, 0x72, 0x2f, 0xb9, 0xd7, 0x0d, 0x2c, 0x08, 0x96, 0x3d, 0x4a, 0x7e, 0xa4, 0xdc, 0x67, 0xf6, 0x02, 0xfb, 0x92, 0x7b, 0x5d, 0xc5, 0x82, 0x60, 0xd9, 0xa3, 0xe4, 0x27, 0xca, 0x7d, 0x96,
0x09, 0xf3, 0x33, 0xba, 0x0d, 0x6d, 0x5e, 0x33, 0x05, 0xd0, 0x2d, 0x46, 0x9a, 0x1e, 0xda, 0x05, 0x30, 0x3f, 0xa3, 0x7b, 0xd0, 0xe0, 0x35, 0x93, 0x02, 0x5d, 0x67, 0xa4, 0xee, 0xa0, 0x3d, 0x80,
0xc8, 0xea, 0x88, 0xfd, 0x6b, 0xf0, 0x7f, 0x52, 0xc6, 0x11, 0x69, 0xb8, 0x88, 0x9d, 0x50, 0xe0, 0xb8, 0x8e, 0xd8, 0xbf, 0x2a, 0xff, 0x27, 0xc5, 0x1c, 0x91, 0x86, 0x8b, 0xd0, 0xf2, 0x05, 0xde,
0xdd, 0xc1, 0x82, 0x40, 0xcf, 0xa0, 0x93, 0x2b, 0x71, 0x74, 0x5a, 0x1c, 0x9d, 0x9b, 0x25, 0x3a, 0x2d, 0x2c, 0x08, 0xf4, 0x12, 0x5a, 0x89, 0x12, 0x47, 0xa7, 0xce, 0xd1, 0xb9, 0x93, 0xa1, 0x13,
0x99, 0x83, 0x1c, 0x12, 0x79, 0x5e, 0x12, 0x68, 0x00, 0x1d, 0x56, 0x90, 0x24, 0xa4, 0x42, 0xb3, 0x3b, 0xc8, 0x21, 0x69, 0x2e, 0x32, 0x02, 0x0d, 0xa0, 0xc5, 0x0a, 0x92, 0xf8, 0x54, 0x68, 0x36,
0xcd, 0x35, 0x1f, 0x96, 0x9a, 0xfd, 0x99, 0x93, 0x87, 0x77, 0xd0, 0x17, 0x92, 0xc2, 0x8a, 0x5b, 0xb8, 0xe6, 0x93, 0x4c, 0xb3, 0x3f, 0xb7, 0x92, 0xf0, 0x0e, 0xfa, 0x42, 0x52, 0x58, 0xb1, 0x33,
0x12, 0xda, 0xef, 0x35, 0xe8, 0x0e, 0x48, 0x40, 0x28, 0xb9, 0x1e, 0x89, 0x4a, 0xd4, 0xf5, 0x6b, 0xa2, 0xf7, 0x47, 0x09, 0xda, 0x03, 0xe2, 0x11, 0x4a, 0x6e, 0x46, 0x22, 0x17, 0x75, 0xf9, 0x86,
0xa2, 0xde, 0xbc, 0x32, 0xea, 0xc6, 0x75, 0x51, 0x37, 0xff, 0x77, 0xd4, 0xbb, 0x00, 0x1e, 0x77, 0xa8, 0x2b, 0xd7, 0x46, 0x5d, 0xbd, 0x29, 0xea, 0xda, 0x7f, 0x8e, 0x7a, 0x0f, 0xc0, 0xe1, 0xee,
0xd7, 0xb3, 0xcf, 0x96, 0x1c, 0x2d, 0x09, 0x4b, 0x19, 0xe7, 0x70, 0xa9, 0x99, 0x80, 0x44, 0x34, 0x3a, 0xe6, 0xd9, 0x9a, 0xa3, 0x25, 0x61, 0x29, 0xe6, 0x1c, 0xae, 0x7b, 0x3a, 0x20, 0x11, 0xcd,
0xcf, 0xa3, 0x78, 0xf8, 0x96, 0x90, 0x56, 0x3d, 0xaf, 0xaf, 0x79, 0xae, 0xfd, 0x51, 0x87, 0xde, 0xab, 0x20, 0x1c, 0xbe, 0x27, 0xa4, 0xa2, 0xe7, 0xe5, 0x0d, 0xcf, 0x7b, 0x7f, 0x96, 0xa1, 0x33,
0xc0, 0x4f, 0xdc, 0x28, 0xf6, 0x72, 0x3b, 0x3d, 0xa8, 0xfb, 0x5e, 0xf6, 0xbc, 0xeb, 0xbe, 0xc7, 0x70, 0x23, 0x3b, 0x08, 0x9d, 0xc4, 0x4e, 0x07, 0xca, 0xae, 0x13, 0x3f, 0xef, 0xb2, 0xeb, 0xf0,
0xcb, 0x23, 0x2f, 0x69, 0x29, 0x2b, 0xd8, 0x7b, 0x20, 0x51, 0x7f, 0x4e, 0x12, 0xea, 0xcc, 0x17, 0xf2, 0x48, 0x4a, 0x5a, 0x8a, 0x0b, 0xf6, 0x21, 0x48, 0xd4, 0x5d, 0x90, 0x88, 0x5a, 0x8b, 0x65,
0x39, 0x1c, 0x05, 0x03, 0xed, 0xc3, 0x76, 0x41, 0xb0, 0xf2, 0x23, 0x79, 0xa1, 0xac, 0xb3, 0xd9, 0x02, 0x47, 0xca, 0x40, 0xfb, 0xb0, 0x93, 0x12, 0xac, 0xfc, 0x48, 0x52, 0x28, 0x9b, 0x6c, 0xf6,
0x43, 0xca, 0xf2, 0xc4, 0xd1, 0x91, 0x70, 0x4e, 0xa2, 0xcf, 0xa0, 0xe5, 0xa4, 0x74, 0x16, 0xc5, 0x90, 0xe2, 0x3c, 0x71, 0x74, 0x24, 0x9c, 0x90, 0xe8, 0x73, 0xa8, 0x5b, 0x2b, 0x3a, 0x0f, 0x42,
0x3c, 0x7c, 0xf9, 0xc9, 0xfd, 0x12, 0xb6, 0x55, 0x7f, 0x75, 0x2e, 0x85, 0x33, 0x69, 0xf4, 0x25, 0x1e, 0x7e, 0xf3, 0xf9, 0xa3, 0x0c, 0xb6, 0xa2, 0xbf, 0x2a, 0x97, 0xc2, 0xb1, 0x34, 0xfa, 0x0a,
0x48, 0x31, 0x39, 0x27, 0x31, 0x09, 0x5d, 0x51, 0x2d, 0x72, 0xb5, 0x5a, 0x56, 0x55, 0x71, 0x2e, 0xa4, 0x90, 0x9c, 0x93, 0x90, 0xf8, 0xb6, 0xa8, 0x96, 0x66, 0xbe, 0x5a, 0x8a, 0xaa, 0x38, 0x11,
0x88, 0x4b, 0x1d, 0x34, 0x00, 0xd9, 0xa1, 0xd4, 0x71, 0x67, 0x73, 0x12, 0xd2, 0x44, 0xdd, 0xda, 0xc4, 0x99, 0x0e, 0x1a, 0x40, 0xd3, 0xa2, 0xd4, 0xb2, 0xe7, 0x0b, 0xe2, 0xd3, 0x48, 0xd9, 0xee,
0xdb, 0xdc, 0x97, 0x9f, 0x68, 0x57, 0xde, 0x5e, 0x88, 0xe2, 0xaa, 0x9a, 0xf6, 0x57, 0x0d, 0x76, 0x56, 0xf6, 0x9b, 0xcf, 0x7b, 0xd7, 0xde, 0x9e, 0x8a, 0xe2, 0xbc, 0x5a, 0xef, 0xaf, 0x12, 0xec,
0xde, 0xe4, 0xe7, 0x9b, 0xd0, 0x0d, 0x9d, 0x79, 0x81, 0x2e, 0x3b, 0xa3, 0x47, 0xd0, 0xf5, 0xfc, 0x5e, 0xe5, 0xe7, 0x55, 0xe8, 0xfa, 0xd6, 0x22, 0x45, 0x97, 0x9d, 0xd1, 0x53, 0x68, 0x3b, 0x6e,
0xc4, 0x8d, 0xfd, 0xb9, 0x1f, 0x3a, 0x34, 0x8a, 0x33, 0x84, 0x57, 0x99, 0xe8, 0x2e, 0x6c, 0x85, 0x64, 0x87, 0xee, 0xc2, 0xf5, 0x2d, 0x1a, 0x84, 0x31, 0xc2, 0x45, 0x26, 0x7a, 0x00, 0xdb, 0xbe,
0xbe, 0x7b, 0xc9, 0xb5, 0x05, 0xbc, 0x05, 0xcd, 0xf2, 0xe3, 0x7c, 0xef, 0x50, 0x27, 0x9e, 0xc6, 0x6b, 0x5f, 0x72, 0x6d, 0x01, 0x6f, 0x4a, 0xb3, 0xfc, 0x58, 0x3f, 0x58, 0xd4, 0x0a, 0x67, 0xa1,
0x41, 0x86, 0x6c, 0xc9, 0x40, 0x07, 0x80, 0x04, 0xc1, 0x9b, 0xdc, 0x38, 0xeb, 0x64, 0x2d, 0x5e, 0x17, 0x23, 0x9b, 0x31, 0xd0, 0x01, 0x20, 0x41, 0xf0, 0x26, 0x37, 0x89, 0x3b, 0x59, 0x9d, 0xd7,
0xbb, 0x6f, 0xf8, 0xc3, 0x6e, 0x0a, 0x22, 0xd7, 0x09, 0x98, 0xb1, 0xb6, 0xb8, 0x29, 0xa7, 0xb5, 0xee, 0x15, 0x7f, 0xd8, 0x4d, 0x5e, 0x60, 0x5b, 0x1e, 0x33, 0xd6, 0x10, 0x37, 0x25, 0x74, 0x2f,
0x08, 0x6e, 0x5f, 0x01, 0x2a, 0x73, 0xa2, 0x28, 0xb4, 0x2c, 0xe2, 0xca, 0x9b, 0xb9, 0x07, 0x92, 0x80, 0x7b, 0xd7, 0x80, 0xca, 0x9c, 0x48, 0x0b, 0x2d, 0x8e, 0x38, 0xf7, 0x66, 0x1e, 0x82, 0x64,
0x3b, 0x73, 0xc2, 0x90, 0x04, 0x66, 0x51, 0x97, 0x05, 0x83, 0x15, 0xc6, 0x45, 0xea, 0x07, 0x9e, 0xcf, 0x2d, 0xdf, 0x27, 0x9e, 0x9e, 0xd6, 0x65, 0xca, 0x60, 0x85, 0x71, 0xb1, 0x72, 0x3d, 0x47,
0x59, 0x34, 0xfa, 0x8c, 0xd4, 0xfe, 0xa9, 0x81, 0x7a, 0x55, 0x0e, 0xfe, 0x83, 0xee, 0x8a, 0x0b, 0x4f, 0x1b, 0x7d, 0x4c, 0xf6, 0xfe, 0x2e, 0x81, 0x72, 0x5d, 0x0e, 0xfe, 0x85, 0x6e, 0xc1, 0x85,
0xeb, 0xc5, 0x8f, 0x14, 0xd8, 0x4c, 0xe3, 0x20, 0xbb, 0x80, 0x1d, 0x59, 0xa4, 0xe7, 0x7e, 0x40, 0xcd, 0xe2, 0x47, 0x32, 0x54, 0x56, 0xa1, 0x17, 0x5f, 0xc0, 0x8e, 0x2c, 0xd2, 0x73, 0xd7, 0x23,
0x46, 0x15, 0x4c, 0x73, 0x9a, 0x65, 0x85, 0x9d, 0x27, 0xfe, 0x4f, 0xe4, 0x70, 0x49, 0x49, 0xc2, 0xa3, 0x1c, 0xa6, 0x09, 0xcd, 0xb2, 0xc2, 0xce, 0x53, 0xf7, 0x67, 0x72, 0xb8, 0xa6, 0x24, 0xe2,
0x71, 0x6d, 0xe0, 0x55, 0x26, 0xda, 0x83, 0x6a, 0xe7, 0xc9, 0xde, 0x6e, 0x95, 0x55, 0x1d, 0x1e, 0xb8, 0x56, 0x71, 0x91, 0x89, 0xba, 0x90, 0xef, 0x3c, 0xf1, 0xdb, 0xcd, 0xb3, 0xf2, 0xc3, 0xa3,
0xed, 0xd5, 0xe1, 0x51, 0xc5, 0x79, 0x6b, 0x0d, 0xe7, 0x3f, 0x6b, 0xd0, 0x99, 0x86, 0xe7, 0x69, 0x51, 0x1c, 0x1e, 0x79, 0x9c, 0xb7, 0x37, 0x70, 0x7e, 0x57, 0x82, 0xd6, 0xcc, 0x3f, 0x5f, 0x85,
0x1c, 0x10, 0xef, 0xa5, 0x1f, 0x5e, 0xe6, 0xce, 0xd7, 0x4a, 0xe7, 0x77, 0xa0, 0x49, 0x7d, 0x1a, 0x1e, 0x71, 0xde, 0xb8, 0xfe, 0x65, 0xe2, 0x7c, 0x29, 0x73, 0x7e, 0x17, 0x6a, 0xd4, 0xa5, 0x5e,
0xe4, 0xb5, 0x24, 0x08, 0xe6, 0x90, 0x47, 0x58, 0xdd, 0x2c, 0xd8, 0x2c, 0xc9, 0x82, 0xad, 0xb2, 0x52, 0x4b, 0x82, 0x60, 0x0e, 0x39, 0x84, 0xd5, 0xcd, 0x92, 0xcd, 0x92, 0x38, 0xd8, 0x3c, 0x0b,
0xd0, 0x87, 0xf0, 0x1e, 0x9d, 0xa5, 0xf3, 0xb3, 0xd0, 0xf1, 0x03, 0x3b, 0x77, 0x4d, 0x74, 0x32, 0x7d, 0x04, 0xb7, 0xe8, 0x7c, 0xb5, 0x38, 0xf3, 0x2d, 0xd7, 0x33, 0x13, 0xd7, 0x44, 0x27, 0x93,
0xa5, 0xf8, 0x31, 0x2e, 0x66, 0xf5, 0x76, 0x29, 0x2c, 0x26, 0xae, 0x18, 0xad, 0xbd, 0x82, 0xfd, 0xd3, 0x1f, 0x93, 0x74, 0x56, 0xef, 0x64, 0xc2, 0x62, 0xe2, 0x8a, 0xd1, 0xda, 0x49, 0xd9, 0x6f,
0x9a, 0x8f, 0xde, 0x0f, 0xa0, 0x54, 0xb6, 0xb3, 0x21, 0x2c, 0x06, 0x6c, 0x69, 0xe0, 0x98, 0xb3, 0xf9, 0xe8, 0xfd, 0x3f, 0x64, 0xca, 0x66, 0x3c, 0x84, 0xc5, 0x80, 0xcd, 0x0c, 0x1c, 0x73, 0x76,
0xb5, 0x5f, 0x24, 0x90, 0x2b, 0x7d, 0xfc, 0x8a, 0x4e, 0xb6, 0xd2, 0x73, 0xea, 0xfc, 0x4f, 0xa5, 0xef, 0x9d, 0x04, 0xcd, 0x5c, 0x1f, 0xbf, 0xa6, 0x93, 0x15, 0x7a, 0x4e, 0x99, 0xff, 0xc9, 0xf5,
0xe7, 0xe4, 0x43, 0x6c, 0xb3, 0x32, 0xc4, 0x1e, 0x80, 0x1c, 0x93, 0x64, 0x11, 0x85, 0x09, 0xb1, 0x9c, 0x64, 0x88, 0x55, 0x72, 0x43, 0xec, 0x31, 0x34, 0x43, 0x12, 0x2d, 0x03, 0x3f, 0x22, 0x26,
0x69, 0x94, 0x25, 0x14, 0x72, 0x96, 0x15, 0xb1, 0x7d, 0x82, 0x84, 0x89, 0xcd, 0x9f, 0x50, 0xd6, 0x0d, 0xe2, 0x84, 0x42, 0xc2, 0x32, 0x02, 0xb6, 0x4f, 0x10, 0x3f, 0x32, 0xf9, 0x13, 0x8a, 0xfb,
0x7f, 0x48, 0x98, 0xf0, 0x6c, 0x57, 0x46, 0x41, 0x6b, 0x65, 0x14, 0xac, 0x77, 0xf5, 0xf6, 0x3b, 0x0f, 0xf1, 0x23, 0x9e, 0xed, 0xdc, 0x28, 0xa8, 0x17, 0x46, 0xc1, 0x66, 0x57, 0x6f, 0x7c, 0xf0,
0xcf, 0xb2, 0xad, 0x77, 0x99, 0x65, 0xe8, 0x29, 0xb4, 0x13, 0xb1, 0x91, 0xa9, 0x12, 0x6f, 0x6f, 0x2c, 0xdb, 0xfe, 0x90, 0x59, 0x86, 0x5e, 0x40, 0x23, 0x12, 0x1b, 0x99, 0x22, 0xf1, 0xf6, 0xa6,
0x6a, 0x69, 0x60, 0x75, 0x55, 0x3b, 0xde, 0xc0, 0xb9, 0x28, 0x3a, 0x80, 0x26, 0x5f, 0x75, 0x54, 0x64, 0x06, 0x8a, 0xab, 0xda, 0xf1, 0x16, 0x4e, 0x44, 0xd1, 0x01, 0xd4, 0xf8, 0xaa, 0xa3, 0x00,
0xe0, 0x3a, 0xb7, 0xd6, 0x76, 0xac, 0x52, 0x43, 0x88, 0x31, 0x79, 0x87, 0x2d, 0x1c, 0xaa, 0xbc, 0xd7, 0xb9, 0xbb, 0xb1, 0x63, 0x65, 0x1a, 0x42, 0x8c, 0xc9, 0x5b, 0x6c, 0xe1, 0x50, 0x9a, 0x9b,
0x2e, 0x5f, 0x5d, 0x64, 0x98, 0x3c, 0x17, 0x43, 0xf7, 0x41, 0x72, 0xa3, 0xf9, 0x3c, 0x0d, 0x7d, 0xf2, 0xf9, 0x45, 0x86, 0xc9, 0x73, 0x31, 0xf4, 0x08, 0x24, 0x3b, 0x58, 0x2c, 0x56, 0xbe, 0x4b,
0xba, 0x54, 0x3b, 0xac, 0x76, 0x8e, 0x37, 0x70, 0xc9, 0x42, 0x7d, 0xd8, 0xf6, 0xc4, 0xa3, 0xcd, 0xd7, 0x4a, 0x8b, 0xd5, 0xce, 0xf1, 0x16, 0xce, 0x58, 0xa8, 0x0f, 0x3b, 0x8e, 0x78, 0xb4, 0xc9,
0xd7, 0x50, 0xd5, 0x5d, 0xf7, 0x7e, 0xf5, 0x55, 0x1f, 0x6f, 0xe0, 0x9e, 0xb7, 0x3a, 0x99, 0x8a, 0x1a, 0xaa, 0xd8, 0x9b, 0xde, 0x17, 0x5f, 0xf5, 0xf1, 0x16, 0xee, 0x38, 0xc5, 0xc9, 0x94, 0x8e,
0x31, 0xdb, 0xad, 0x8e, 0xd9, 0x87, 0xd0, 0xf1, 0xfc, 0x64, 0x11, 0x38, 0x4b, 0x91, 0xc8, 0x5e, 0xd9, 0x76, 0x7e, 0xcc, 0x3e, 0x81, 0x96, 0xe3, 0x46, 0x4b, 0xcf, 0x5a, 0x8b, 0x44, 0x76, 0xe2,
0x56, 0xe1, 0x82, 0xc7, 0x93, 0xb9, 0x80, 0xbd, 0x6c, 0xad, 0xb5, 0x63, 0xf2, 0x5d, 0x4a, 0x12, 0x0a, 0x17, 0x3c, 0x9e, 0xcc, 0x25, 0x74, 0xe3, 0xb5, 0xd6, 0x0c, 0xc9, 0xf7, 0x2b, 0x12, 0x51,
0x6a, 0x2f, 0xe2, 0x68, 0xe1, 0x5c, 0x38, 0x6c, 0xc4, 0x26, 0xd4, 0xa1, 0x44, 0xdd, 0xe6, 0xee, 0x73, 0x19, 0x06, 0x4b, 0xeb, 0xc2, 0x62, 0x23, 0x36, 0xa2, 0x16, 0x25, 0xca, 0x0e, 0x77, 0xe7,
0x3c, 0xae, 0x64, 0x43, 0x68, 0x60, 0xa1, 0x30, 0x2e, 0xe4, 0x27, 0x4c, 0x1c, 0xef, 0xba, 0xd7, 0x59, 0x2e, 0x1b, 0x42, 0x03, 0x0b, 0x85, 0x49, 0x2a, 0x3f, 0x65, 0xe2, 0x78, 0xcf, 0xbe, 0xe9,
0xfd, 0x46, 0x5f, 0x40, 0x2f, 0xcd, 0x5e, 0xab, 0x1d, 0xf8, 0xe1, 0x65, 0xa2, 0x2a, 0x7c, 0x90, 0x37, 0xfa, 0x12, 0x3a, 0xab, 0xf8, 0xb5, 0x9a, 0x9e, 0xeb, 0x5f, 0x46, 0x8a, 0xcc, 0x07, 0x49,
0x54, 0x80, 0xac, 0xbe, 0x66, 0xdc, 0x4d, 0x2b, 0x54, 0xa2, 0xfd, 0x5d, 0x07, 0xb9, 0xbf, 0xd2, 0x0e, 0xc8, 0xfc, 0x6b, 0xc6, 0xed, 0x55, 0x8e, 0x8a, 0x7a, 0xbf, 0x55, 0xa0, 0xd9, 0x2f, 0xf4,
0x33, 0x76, 0xf2, 0x95, 0xaf, 0x7f, 0x32, 0xb2, 0x8c, 0x91, 0x95, 0x2f, 0x7d, 0x3d, 0x00, 0xcb, 0x8c, 0xdd, 0x64, 0xe5, 0xeb, 0x8f, 0x47, 0x86, 0x36, 0x32, 0x92, 0xa5, 0xaf, 0x03, 0x60, 0x68,
0xf8, 0xc6, 0xb2, 0xc7, 0x2f, 0x75, 0x73, 0xa4, 0xd4, 0x90, 0x0c, 0xed, 0x89, 0x65, 0xf6, 0x5f, 0xdf, 0x1a, 0xe6, 0xe4, 0x8d, 0xaa, 0x8f, 0xe4, 0x12, 0x6a, 0x42, 0x63, 0x6a, 0xe8, 0xfd, 0xd7,
0x18, 0x58, 0xa9, 0x23, 0x80, 0xd6, 0xc4, 0xd2, 0xad, 0xe9, 0x44, 0xd9, 0x44, 0x12, 0x34, 0x8d, 0x1a, 0x96, 0xcb, 0x08, 0xa0, 0x3e, 0x35, 0x54, 0x63, 0x36, 0x95, 0x2b, 0x48, 0x82, 0x9a, 0x36,
0xe1, 0xc9, 0x57, 0xa6, 0xd2, 0x40, 0xb7, 0xe1, 0x86, 0x85, 0xf5, 0xd1, 0x44, 0xef, 0x5b, 0xe6, 0x1c, 0x7f, 0xad, 0xcb, 0x55, 0x74, 0x0f, 0x6e, 0x1b, 0x58, 0x1d, 0x4d, 0xd5, 0xbe, 0xa1, 0x8f,
0x09, 0xb3, 0x38, 0x1c, 0xea, 0xa3, 0x81, 0xd2, 0x44, 0xfb, 0xf0, 0x68, 0x72, 0x3a, 0xb1, 0x8c, 0x99, 0xc5, 0xe1, 0x50, 0x1d, 0x0d, 0xe4, 0x1a, 0xda, 0x87, 0xa7, 0xd3, 0xd3, 0xa9, 0xa1, 0x0d,
0xa1, 0x3d, 0x34, 0x26, 0x13, 0xfd, 0xc8, 0x28, 0x6e, 0x1b, 0x63, 0xf3, 0x95, 0x6e, 0x19, 0xf6, 0xcd, 0xa1, 0x36, 0x9d, 0xaa, 0x47, 0x5a, 0x7a, 0xdb, 0x04, 0xeb, 0x27, 0xaa, 0xa1, 0x99, 0x47,
0x11, 0x3e, 0x99, 0x8e, 0x95, 0x16, 0xb3, 0x66, 0x0e, 0xf5, 0x23, 0x43, 0x69, 0xb3, 0x23, 0x5f, 0x78, 0x3c, 0x9b, 0xc8, 0x75, 0x66, 0x4d, 0x1f, 0xaa, 0x47, 0x9a, 0xdc, 0x60, 0x47, 0xbe, 0x86,
0x43, 0x95, 0x2d, 0xd4, 0x05, 0x89, 0x19, 0x9b, 0x8e, 0x4c, 0xeb, 0x54, 0x91, 0xd8, 0xa2, 0xba, 0xca, 0xdb, 0xa8, 0x0d, 0x12, 0x33, 0x36, 0x1b, 0xe9, 0xc6, 0xa9, 0x2c, 0xb1, 0x45, 0x75, 0xc3,
0x66, 0xee, 0x48, 0x1f, 0x2b, 0x80, 0x6e, 0xc0, 0x36, 0xb3, 0xab, 0xf7, 0x2d, 0x1b, 0x1b, 0x5f, 0xdc, 0x91, 0x3a, 0x91, 0x01, 0xdd, 0x86, 0x1d, 0x66, 0x57, 0xed, 0x1b, 0x26, 0xd6, 0xbe, 0x99,
0x4f, 0x8d, 0x89, 0xa5, 0xc8, 0x8c, 0x39, 0x30, 0x27, 0xfd, 0x13, 0x3c, 0xc8, 0xa5, 0x95, 0x0e, 0x69, 0x53, 0x43, 0x6e, 0x32, 0xe6, 0x40, 0x9f, 0xf6, 0xc7, 0x78, 0x90, 0x48, 0xcb, 0x2d, 0x74,
0xba, 0x03, 0x37, 0xcd, 0x81, 0x31, 0xb2, 0x4c, 0xeb, 0xd4, 0x7e, 0x65, 0x60, 0xf3, 0xb9, 0xd9, 0x1f, 0xee, 0xe8, 0x03, 0x6d, 0x64, 0xe8, 0xc6, 0xa9, 0x79, 0xa2, 0x61, 0xfd, 0x95, 0xde, 0x57,
0xd7, 0x99, 0xcf, 0x4a, 0x17, 0x3d, 0x84, 0xdd, 0x35, 0xe3, 0x63, 0x73, 0x34, 0x32, 0x4a, 0xed, 0x99, 0xcf, 0x72, 0x1b, 0x3d, 0x81, 0xbd, 0x0d, 0xe3, 0x13, 0x7d, 0x34, 0xd2, 0x32, 0xed, 0x0e,
0x1e, 0x7a, 0x1f, 0xb4, 0x35, 0x91, 0xe1, 0xd4, 0x9a, 0xea, 0x2f, 0x6d, 0x06, 0x8a, 0x61, 0x4f, 0x7a, 0x0a, 0xdd, 0x0d, 0x91, 0xe1, 0xcc, 0x98, 0xa9, 0x6f, 0x4c, 0xed, 0x84, 0xc5, 0x34, 0xd5,
0xc7, 0x03, 0xdd, 0x32, 0x94, 0xed, 0x43, 0xa9, 0xe8, 0xc8, 0x87, 0xdd, 0x6f, 0xe5, 0x83, 0x8f, 0x46, 0x86, 0xbc, 0x73, 0x45, 0xd0, 0x05, 0x29, 0xb5, 0xdf, 0xd7, 0x26, 0x86, 0x36, 0x90, 0x65,
0x3f, 0xcf, 0x73, 0x74, 0xd6, 0xe2, 0xa7, 0x4f, 0xfe, 0x0d, 0x00, 0x00, 0xff, 0xff, 0x18, 0x57, 0xf4, 0x0c, 0xfe, 0x77, 0x93, 0x24, 0xd6, 0x86, 0xe3, 0x13, 0x6d, 0x20, 0xdf, 0x3a, 0x94, 0xd2,
0x25, 0x82, 0x41, 0x0d, 0x00, 0x00, 0x16, 0x7f, 0xd8, 0xfe, 0xae, 0x79, 0xf0, 0xc9, 0x17, 0x49, 0xd2, 0xcf, 0xea, 0xfc, 0xf4, 0xe9,
0x3f, 0x01, 0x00, 0x00, 0xff, 0xff, 0x5b, 0xb5, 0x91, 0x0c, 0x92, 0x0d, 0x00, 0x00,
} }

View File

@ -180,6 +180,11 @@ message ChatMessage {
IDENTITY_VERIFICATION = 13; IDENTITY_VERIFICATION = 13;
// Only local // Only local
SYSTEM_MESSAGE_PINNED_MESSAGE = 14; SYSTEM_MESSAGE_PINNED_MESSAGE = 14;
SYSTEM_MESSAGE_MUTUAL_STATE_UPDATE = 15; // Only local
SYSTEM_MESSAGE_MUTUAL_EVENT_SENT = 15;
// Only local
SYSTEM_MESSAGE_MUTUAL_EVENT_ACCEPTED = 16;
// Only local
SYSTEM_MESSAGE_MUTUAL_EVENT_REMOVED = 17;
} }
} }