add PinMessage and PinnedMessage (#2180)

* add PinMessage and PinnedMessage

* fix gruop pin messages

* add SkipGroupMessageWrap to pin messages

* update pinMessage ID generation to be symmetric
This commit is contained in:
Andrea Franz 2021-05-14 23:22:50 +02:00 committed by GitHub
parent 6a930ed0c6
commit e9a42bfa2b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 1078 additions and 245 deletions

View File

@ -0,0 +1,60 @@
package common
import (
"crypto/ecdsa"
"github.com/golang/protobuf/proto"
"github.com/status-im/status-go/protocol/protobuf"
)
type PinMessage struct {
protobuf.PinMessage
// ID calculated as keccak256(compressedAuthorPubKey, data) where data is unencrypted payload.
ID string `json:"id"`
// MessageID string `json:"messageID"`
// WhisperTimestamp is a timestamp of a Whisper envelope.
WhisperTimestamp uint64 `json:"whisperTimestamp"`
// From is a public key of the user who pinned the message.
From string `json:"from"`
// The chat id to be stored locally
LocalChatID string `json:"localChatId"`
SigPubKey *ecdsa.PublicKey `json:"-"`
// Identicon of the author
Identicon string `json:"identicon"`
// Random 3 words name
Alias string `json:"alias"`
}
type PinnedMessage struct {
Message
PinnedAt uint64 `json:"pinnedAt"`
}
// WrapGroupMessage indicates whether we should wrap this in membership information
func (m *PinMessage) WrapGroupMessage() bool {
return false
}
// SetMessageType a setter for the MessageType field
// this function is required to implement the ChatEntity interface
func (m *PinMessage) SetMessageType(messageType protobuf.MessageType) {
m.MessageType = messageType
}
func (m *PinMessage) GetGrant() []byte {
return nil
}
// GetProtoBuf returns the struct's embedded protobuf struct
// this function is required to implement the ChatEntity interface
func (m *PinMessage) GetProtobuf() proto.Message {
return &m.PinMessage
}
// GetSigPubKey returns an ecdsa encoded public key
// this function is required to implement the ChatEntity interface
func (m PinMessage) GetSigPubKey() *ecdsa.PublicKey {
return m.SigPubKey
}

View File

@ -38,3 +38,29 @@ func extendMessageFromChat(message *common.Message, chat *Chat, key *ecdsa.Publi
return nil
}
func extendPinMessageFromChat(message *common.PinMessage, chat *Chat, key *ecdsa.PublicKey, timesource common.TimeSource) error {
clock, timestamp := chat.NextClockAndTimestamp(timesource)
message.LocalChatID = chat.ID
message.Clock = clock
message.From = types.EncodeHex(crypto.FromECDSAPub(key))
message.SigPubKey = key
message.WhisperTimestamp = timestamp
identicon, err := identicon.GenerateBase64(message.From)
if err != nil {
return err
}
message.Identicon = identicon
alias, err := alias.GenerateFromPublicKeyString(message.From)
if err != nil {
return err
}
message.Alias = alias
return nil
}

View File

@ -5,10 +5,10 @@ import (
"encoding/hex"
"fmt"
"github.com/status-im/status-go/protocol/transport"
"github.com/pkg/errors"
"go.uber.org/zap"
"github.com/status-im/status-go/eth-node/crypto"
"github.com/status-im/status-go/eth-node/types"
"github.com/status-im/status-go/images"
@ -18,9 +18,9 @@ import (
"github.com/status-im/status-go/protocol/encryption/multidevice"
"github.com/status-im/status-go/protocol/ens"
"github.com/status-im/status-go/protocol/protobuf"
v1protocol "github.com/status-im/status-go/protocol/v1"
"go.uber.org/zap"
"github.com/status-im/status-go/protocol/transport"
v1protocol "github.com/status-im/status-go/protocol/v1"
)
const (
@ -324,6 +324,59 @@ func (m *MessageHandler) HandleSyncInstallationPublicChat(state *ReceivedMessage
return true
}
func (m *MessageHandler) HandlePinMessage(state *ReceivedMessageState, message protobuf.PinMessage) error {
logger := m.logger.With(zap.String("site", "HandlePinMessage"))
logger.Info("Handling pin message")
pinMessage := &common.PinMessage{
PinMessage: message,
// MessageID: message.MessageId,
WhisperTimestamp: state.CurrentMessageState.WhisperTimestamp,
From: state.CurrentMessageState.Contact.ID,
SigPubKey: state.CurrentMessageState.PublicKey,
Identicon: state.CurrentMessageState.Contact.Identicon,
Alias: state.CurrentMessageState.Contact.Alias,
}
chat, err := m.matchChatEntity(pinMessage, state.AllChats, state.AllContacts, state.Timesource)
if err != nil {
return err // matchChatEntity returns a descriptive error message
}
pinMessage.ID, err = generatePinMessageID(&m.identity.PublicKey, pinMessage, chat)
if err != nil {
return err
}
// If deleted-at is greater, ignore message
if chat.DeletedAtClockValue >= pinMessage.Clock {
return nil
}
// Set the LocalChatID for the message
pinMessage.LocalChatID = chat.ID
if c, ok := state.AllChats.Load(chat.ID); ok {
chat = c
}
// Set the LocalChatID for the message
pinMessage.LocalChatID = chat.ID
if chat.LastClockValue < message.Clock {
chat.LastClockValue = message.Clock
}
state.Response.AddPinMessage(pinMessage)
// Set in the modified maps chat
state.Response.AddChat(chat)
state.AllChats.Store(chat.ID, chat)
return nil
}
func (m *MessageHandler) HandleContactUpdate(state *ReceivedMessageState, message protobuf.ContactUpdate) error {
logger := m.logger.With(zap.String("site", "HandleContactUpdate"))
contact := state.CurrentMessageState.Contact

View File

@ -543,6 +543,91 @@ func (db sqlitePersistence) MessageByChatID(chatID string, currCursor string, li
return result, newCursor, nil
}
// PinnedMessageByChatID returns all pinned messages for a given chatID in descending order.
// Ordering is accomplished using two concatenated values: ClockValue and ID.
// These two values are also used to compose a cursor which is returned to the result.
func (db sqlitePersistence) PinnedMessageByChatIDs(chatIDs []string, currCursor string, limit int) ([]*common.PinnedMessage, string, error) {
cursorWhere := ""
if currCursor != "" {
cursorWhere = "AND cursor <= ?" //nolint: goconst
}
allFields := db.tableUserMessagesAllFieldsJoin()
args := make([]interface{}, len(chatIDs))
for i, v := range chatIDs {
args[i] = v
}
if currCursor != "" {
args = append(args, currCursor)
}
// Build a new column `cursor` at the query time by having a fixed-sized clock value at the beginning
// concatenated with message ID. Results are sorted using this new column.
// This new column values can also be returned as a cursor for subsequent requests.
rows, err := db.db.Query(
fmt.Sprintf(`
SELECT
%s,
pm.clock_value as pinnedAt,
substr('0000000000000000000000000000000000000000000000000000000000000000' || m1.clock_value, -64, 64) || m1.id as cursor
FROM
pin_messages pm
JOIN
user_messages m1
ON
pm.message_id = m1.id
LEFT JOIN
user_messages m2
ON
m1.response_to = m2.id
LEFT JOIN
contacts c
ON
m1.source = c.id
WHERE
pm.pinned = 1
AND NOT(m1.hide) AND m1.local_chat_id IN %s %s
ORDER BY cursor DESC
LIMIT ?
`, allFields, "(?"+strings.Repeat(",?", len(chatIDs)-1)+")", cursorWhere),
append(args, limit+1)..., // take one more to figure our whether a cursor should be returned
)
if err != nil {
return nil, "", err
}
defer rows.Close()
var (
result []*common.PinnedMessage
cursors []string
)
for rows.Next() {
var (
message common.Message
pinnedAt uint64
cursor string
)
if err := db.tableUserMessagesScanAllFields(rows, &message, &pinnedAt, &cursor); err != nil {
return nil, "", err
}
pinnedMessage := &common.PinnedMessage{
Message: message,
PinnedAt: pinnedAt,
}
result = append(result, pinnedMessage)
cursors = append(cursors, cursor)
}
var newCursor string
if len(result) > limit && cursors != nil {
newCursor = cursors[limit]
result = result[:limit]
}
return result, newCursor, nil
}
func (db sqlitePersistence) PinnedMessageByChatID(chatID string, currCursor string, limit int) ([]*common.PinnedMessage, string, error) {
return db.PinnedMessageByChatIDs([]string{chatID}, currCursor, limit)
}
// MessageByChatIDs returns all messages for a given chatIDs in descending order.
// Ordering is accomplished using two concatenated values: ClockValue and ID.
// These two values are also used to compose a cursor which is returned to the result.
@ -807,6 +892,76 @@ func (db sqlitePersistence) SaveMessages(messages []*common.Message) (err error)
return
}
func (db sqlitePersistence) SavePinMessages(messages []*common.PinMessage) (err error) {
tx, err := db.db.BeginTx(context.Background(), nil)
if err != nil {
return
}
defer func() {
if err == nil {
err = tx.Commit()
return
}
// don't shadow original error
_ = tx.Rollback()
}()
// select
selectQuery := "SELECT clock_value FROM pin_messages WHERE id = ?"
// insert
allInsertFields := `id, message_id, whisper_timestamp, chat_id, local_chat_id, clock_value, pinned`
insertValues := strings.Repeat("?, ", strings.Count(allInsertFields, ",")) + "?"
insertQuery := "INSERT INTO pin_messages(" + allInsertFields + ") VALUES (" + insertValues + ")" // nolint: gosec
insertStmt, err := tx.Prepare(insertQuery)
if err != nil {
return
}
// update
updateQuery := "UPDATE pin_messages SET pinned = ?, clock_value = ? WHERE id = ?"
updateStmt, err := tx.Prepare(updateQuery)
if err != nil {
return
}
for _, message := range messages {
row := tx.QueryRow(selectQuery, message.ID)
var existingClock uint64
switch err = row.Scan(&existingClock); err {
case sql.ErrNoRows:
// not found, insert new record
allValues := []interface{}{
message.ID,
message.MessageId,
message.WhisperTimestamp,
message.ChatId,
message.LocalChatID,
message.Clock,
message.Pinned,
}
_, err = insertStmt.Exec(allValues...)
if err != nil {
return
}
case nil:
// found, update if current message is more recent, otherwise skip
if existingClock < message.Clock {
// update
_, err = updateStmt.Exec(message.Pinned, message.Clock, message.ID)
if err != nil {
return
}
}
default:
return
}
}
return
}
func (db sqlitePersistence) DeleteMessage(id string) error {
_, err := db.db.Exec(`DELETE FROM user_messages WHERE id = ?`, id)
return err
@ -838,6 +993,12 @@ func (db sqlitePersistence) deleteMessagesByChatID(id string, tx *sql.Tx) (err e
}
_, err = tx.Exec(`DELETE FROM user_messages WHERE local_chat_id = ?`, id)
if err != nil {
return
}
_, err = tx.Exec(`DELETE FROM pin_messages WHERE local_chat_id = ?`, id)
return
}

View File

@ -2539,6 +2539,15 @@ func (m *Messenger) handleRetrievedMessages(chatWithMessages map[transport.Filte
continue
}
case protobuf.PinMessage:
pinMessage := msg.ParsedMessage.Interface().(protobuf.PinMessage)
err = m.handler.HandlePinMessage(messageState, pinMessage)
if err != nil {
logger.Warn("failed to handle PinMessage", zap.Error(err))
allMessagesProcessed = false
continue
}
case protobuf.PairInstallation:
if !common.IsPubKeyEqual(messageState.CurrentMessageState.PublicKey, &m.identity.PublicKey) {
logger.Warn("not coming from us, ignoring")
@ -2939,6 +2948,12 @@ func (m *Messenger) handleRetrievedMessages(chatWithMessages map[transport.Filte
return nil, err
}
}
if len(messageState.Response.pinMessages) > 0 {
err = m.SavePinMessages(messageState.Response.PinMessages())
if err != nil {
return nil, err
}
}
for _, emojiReaction := range messageState.EmojiReactions {
messageState.Response.EmojiReactions = append(messageState.Response.EmojiReactions, emojiReaction)

View File

@ -0,0 +1,109 @@
package protocol
import (
"context"
"crypto/ecdsa"
"testing"
"github.com/stretchr/testify/suite"
"go.uber.org/zap"
gethbridge "github.com/status-im/status-go/eth-node/bridge/geth"
"github.com/status-im/status-go/eth-node/crypto"
"github.com/status-im/status-go/eth-node/types"
"github.com/status-im/status-go/protocol/common"
"github.com/status-im/status-go/protocol/tt"
"github.com/status-im/status-go/waku"
)
func TestMessengerPinMessageSuite(t *testing.T) {
suite.Run(t, new(MessengerPinMessageSuite))
}
type MessengerPinMessageSuite struct {
suite.Suite
m *Messenger // main instance of Messenger
privateKey *ecdsa.PrivateKey // private key for the main instance of Messenger
// If one wants to send messages between different instances of Messenger,
// a single waku service should be shared.
shh types.Waku
logger *zap.Logger
}
func (s *MessengerPinMessageSuite) SetupTest() {
s.logger = tt.MustCreateTestLogger()
config := waku.DefaultConfig
config.MinimumAcceptedPoW = 0
shh := waku.New(&config, s.logger)
s.shh = gethbridge.NewGethWakuWrapper(shh)
s.Require().NoError(shh.Start(nil))
s.m = s.newMessenger()
s.privateKey = s.m.identity
_, err := s.m.Start()
s.Require().NoError(err)
}
func (s *MessengerPinMessageSuite) TearDownTest() {
s.Require().NoError(s.m.Shutdown())
}
func (s *MessengerPinMessageSuite) newMessenger() *Messenger {
privateKey, err := crypto.GenerateKey()
s.Require().NoError(err)
messenger, err := newMessengerWithKey(s.shh, privateKey, s.logger, nil)
s.Require().NoError(err)
return messenger
}
func (s *MessengerPinMessageSuite) TestPinMessage() {
theirMessenger := s.newMessenger()
_, err := theirMessenger.Start()
s.Require().NoError(err)
theirChat := CreateOneToOneChat("Their 1TO1", &s.privateKey.PublicKey, s.m.transport)
err = theirMessenger.SaveChat(theirChat)
s.Require().NoError(err)
ourChat := CreateOneToOneChat("Our 1TO1", &theirMessenger.identity.PublicKey, s.m.transport)
err = s.m.SaveChat(ourChat)
s.Require().NoError(err)
inputMessage := buildTestMessage(*theirChat)
sendResponse, err := theirMessenger.SendChatMessage(context.Background(), inputMessage)
s.NoError(err)
s.Require().Len(sendResponse.Messages, 1)
response, err := WaitOnMessengerResponse(
s.m,
func(r *MessengerResponse) bool { return len(r.Messages) > 0 },
"no messages",
)
s.Require().NoError(err)
s.Require().Len(response.Chats(), 1)
pinMessage := &common.PinMessage{
LocalChatID: theirChat.ID,
}
pinMessage.MessageId = inputMessage.ID
pinMessage.Pinned = true
pinMessage.ChatId = theirChat.ID
sendResponse, err = theirMessenger.SendPinMessage(context.Background(), pinMessage)
s.NoError(err)
s.Require().Len(sendResponse.PinMessages(), 1)
// Wait for the message to reach its destination
response, err = WaitOnMessengerResponse(
s.m,
func(r *MessengerResponse) bool {
return len(r.PinMessages()) > 0
},
"pin message not received",
)
s.Require().NoError(err)
receivedPinMessage := response.PinMessages()[0]
s.Require().True(receivedPinMessage.Pinned)
}

View File

@ -0,0 +1,127 @@
package protocol
import (
"bytes"
"context"
"crypto/ecdsa"
"crypto/sha256"
"errors"
"fmt"
gethcommon "github.com/ethereum/go-ethereum/common"
"github.com/status-im/status-go/eth-node/crypto"
"github.com/status-im/status-go/protocol/common"
"github.com/status-im/status-go/protocol/protobuf"
)
// SendPinMessage sends the PinMessage to the corresponding chat
func (m *Messenger) SendPinMessage(ctx context.Context, message *common.PinMessage) (*MessengerResponse, error) {
m.mutex.Lock()
defer m.mutex.Unlock()
return m.sendPinMessage(ctx, message)
}
func (m *Messenger) sendPinMessage(ctx context.Context, message *common.PinMessage) (*MessengerResponse, error) {
var response MessengerResponse
// A valid added chat is required.
chat, ok := m.allChats.Load(message.ChatId)
if !ok {
return nil, errors.New("chat not found")
}
err := m.handleStandaloneChatIdentity(chat)
if err != nil {
return nil, err
}
err = extendPinMessageFromChat(message, chat, &m.identity.PublicKey, m.getTimesource())
if err != nil {
return nil, err
}
message.ID, err = generatePinMessageID(&m.identity.PublicKey, message, chat)
if err != nil {
return nil, err
}
encodedMessage, err := m.encodeChatEntity(chat, message)
if err != nil {
return nil, err
}
rawMessage := common.RawMessage{
LocalChatID: chat.ID,
Payload: encodedMessage,
MessageType: protobuf.ApplicationMetadataMessage_PIN_MESSAGE,
SkipGroupMessageWrap: true,
ResendAutomatically: true,
}
_, err = m.dispatchMessage(ctx, rawMessage)
if err != nil {
return nil, err
}
err = m.persistence.SavePinMessages([]*common.PinMessage{message})
if err != nil {
return nil, err
}
response.AddPinMessage(message)
response.AddChat(chat)
return &response, m.saveChat(chat)
}
func (m *Messenger) PinnedMessageByChatID(chatID, cursor string, limit int) ([]*common.PinnedMessage, string, error) {
chat, err := m.persistence.Chat(chatID)
if err != nil {
return nil, "", err
}
if chat.Timeline() {
var chatIDs = []string{"@" + contactIDFromPublicKey(&m.identity.PublicKey)}
contacts, err := m.persistence.Contacts()
if err != nil {
return nil, "", err
}
for _, contact := range contacts {
if contact.IsAdded() {
chatIDs = append(chatIDs, "@"+contact.ID)
}
}
return m.persistence.PinnedMessageByChatIDs(chatIDs, cursor, limit)
}
return m.persistence.PinnedMessageByChatID(chatID, cursor, limit)
}
func (m *Messenger) SavePinMessages(messages []*common.PinMessage) error {
return m.persistence.SavePinMessages(messages)
}
func generatePinMessageID(pubKey *ecdsa.PublicKey, pm *common.PinMessage, chat *Chat) (string, error) {
data := gethcommon.FromHex(pm.MessageId)
switch {
case chat.ChatType == ChatTypeOneToOne:
ourPubKey := crypto.FromECDSAPub(pubKey)
tmpPubKey, err := chat.PublicKey()
if err != nil {
return "", err
}
theirPubKey := crypto.FromECDSAPub(tmpPubKey)
if bytes.Compare(ourPubKey, theirPubKey) < 0 {
data = append(data, ourPubKey...) // our key
data = append(data, theirPubKey...) // their key
} else {
data = append(data, theirPubKey...) // their key
data = append(data, ourPubKey...) // our key
}
default:
data = append(data, []byte(chat.ID)...)
}
id := sha256.Sum256(data)
idString := fmt.Sprintf("%x", id)
return idString, nil
}

View File

@ -32,6 +32,7 @@ type MessengerResponse struct {
removedChats map[string]bool
communities map[string]*communities.Community
activityCenterNotifications map[string]*ActivityCenterNotification
pinMessages map[string]*common.PinMessage
}
func (r *MessengerResponse) MarshalJSON() ([]byte, error) {
@ -39,6 +40,7 @@ func (r *MessengerResponse) MarshalJSON() ([]byte, error) {
Chats []*Chat `json:"chats,omitempty"`
RemovedChats []string `json:"removedChats,omitempty"`
Messages []*common.Message `json:"messages,omitempty"`
PinMessages []*common.PinMessage `json:"pinMessages,omitempty"`
Contacts []*Contact `json:"contacts,omitempty"`
Installations []*multidevice.Installation `json:"installations,omitempty"`
EmojiReactions []*EmojiReaction `json:"emojiReactions,omitempty"`
@ -75,6 +77,7 @@ func (r *MessengerResponse) MarshalJSON() ([]byte, error) {
responseItem.Communities = r.Communities()
responseItem.RemovedChats = r.RemovedChats()
responseItem.ActivityCenterNotifications = r.ActivityCenterNotifications()
responseItem.PinMessages = r.PinMessages()
return json.Marshal(responseItem)
}
@ -111,9 +114,18 @@ func (r *MessengerResponse) Notifications() []*localnotifications.Notification {
return notifications
}
func (r *MessengerResponse) PinMessages() []*common.PinMessage {
var pinMessages []*common.PinMessage
for _, pm := range r.pinMessages {
pinMessages = append(pinMessages, pm)
}
return pinMessages
}
func (r *MessengerResponse) IsEmpty() bool {
return len(r.chats)+
len(r.Messages)+
len(r.pinMessages)+
len(r.Contacts)+
len(r.Installations)+
len(r.Invitations)+
@ -154,6 +166,7 @@ func (r *MessengerResponse) Merge(response *MessengerResponse) error {
r.overrideFilters(response.Filters)
r.overrideRemovedFilters(response.Filters)
r.AddCommunities(response.Communities())
r.AddPinMessages(response.PinMessages())
return nil
}
@ -287,3 +300,17 @@ func (r *MessengerResponse) ActivityCenterNotifications() []*ActivityCenterNotif
}
return ns
}
func (r *MessengerResponse) AddPinMessage(pm *common.PinMessage) {
if r.pinMessages == nil {
r.pinMessages = make(map[string]*common.PinMessage)
}
r.pinMessages[pm.ID] = pm
}
func (r *MessengerResponse) AddPinMessages(pms []*common.PinMessage) {
for _, pm := range pms {
r.AddPinMessage(pm)
}
}

View File

@ -1,41 +1,40 @@
// Code generated by go-bindata. DO NOT EDIT.
// Code generated for package migrations by go-bindata DO NOT EDIT. (@generated)
// sources:
// 000001_init.down.db.sql (65B)
// 000001_init.up.db.sql (2.719kB)
// 000002_add_last_ens_clock_value.up.sql (77B)
// 1586358095_add_replace.up.sql (224B)
// 1588665364_add_image_data.up.sql (186B)
// 1589365189_add_pow_target.up.sql (66B)
// 1591277220_add_index_messages.up.sql (240B)
// 1593087212_add_mute_chat_and_raw_message_fields.up.sql (215B)
// 1595862781_add_audio_data.up.sql (246B)
// 1595865249_create_emoji_reactions_table.up.sql (300B)
// 1596805115_create_group_chat_invitations_table.up.sql (231B)
// 1597322655_add_invitation_admin_chat_field.up.sql (54B)
// 1597757544_add_nickname.up.sql (52B)
// 1598955122_add_mentions.up.sql (52B)
// 1599641390_add_emoji_reactions_index.up.sql (126B)
// 1599720851_add_seen_index_remove_long_messages.up.sql (150B)
// 1603198582_add_profile_chat_field.up.sql (45B)
// 1603816533_add_links.up.sql (48B)
// 1603888149_create_chat_identity_last_published_table.up.sql (407B)
// 1605075346_add_communities.up.sql (6.971kB)
// 1610117927_add_message_cache.up.sql (142B)
// 1610959908_add_dont_wrap_to_raw_messages.up.sql (83B)
// 1610960912_add_send_on_personal_topic.up.sql (82B)
// 1612870480_add_datasync_id.up.sql (111B)
// 1614152139_add_communities_request_to_join.up.sql (831B)
// 1615374373_add_confirmations.up.sql (227B)
// 1617694931_add_notification_center.up.sql (572B)
// README.md (554B)
// doc.go (850B)
// 000001_init.down.db.sql
// 000001_init.up.db.sql
// 000002_add_last_ens_clock_value.up.sql
// 1586358095_add_replace.up.sql
// 1588665364_add_image_data.up.sql
// 1589365189_add_pow_target.up.sql
// 1591277220_add_index_messages.up.sql
// 1593087212_add_mute_chat_and_raw_message_fields.up.sql
// 1595862781_add_audio_data.up.sql
// 1595865249_create_emoji_reactions_table.up.sql
// 1596805115_create_group_chat_invitations_table.up.sql
// 1597322655_add_invitation_admin_chat_field.up.sql
// 1597757544_add_nickname.up.sql
// 1598955122_add_mentions.up.sql
// 1599641390_add_emoji_reactions_index.up.sql
// 1599720851_add_seen_index_remove_long_messages.up.sql
// 1603198582_add_profile_chat_field.up.sql
// 1603816533_add_links.up.sql
// 1603888149_create_chat_identity_last_published_table.up.sql
// 1605075346_add_communities.up.sql
// 1610117927_add_message_cache.up.sql
// 1610959908_add_dont_wrap_to_raw_messages.up.sql
// 1610960912_add_send_on_personal_topic.up.sql
// 1612870480_add_datasync_id.up.sql
// 1614152139_add_communities_request_to_join.up.sql
// 1615374373_add_confirmations.up.sql
// 1617694931_add_notification_center.up.sql
// 1618923660_create_pin_messages.up.sql
// README.md
// doc.go
package migrations
import (
"bytes"
"compress/gzip"
"crypto/sha256"
"fmt"
"io"
"io/ioutil"
@ -48,7 +47,7 @@ import (
func bindataRead(data []byte, name string) ([]byte, error) {
gz, err := gzip.NewReader(bytes.NewBuffer(data))
if err != nil {
return nil, fmt.Errorf("read %q: %v", name, err)
return nil, fmt.Errorf("Read %q: %v", name, err)
}
var buf bytes.Buffer
@ -56,7 +55,7 @@ func bindataRead(data []byte, name string) ([]byte, error) {
clErr := gz.Close()
if err != nil {
return nil, fmt.Errorf("read %q: %v", name, err)
return nil, fmt.Errorf("Read %q: %v", name, err)
}
if clErr != nil {
return nil, err
@ -66,9 +65,8 @@ func bindataRead(data []byte, name string) ([]byte, error) {
}
type asset struct {
bytes []byte
info os.FileInfo
digest [sha256.Size]byte
bytes []byte
info os.FileInfo
}
type bindataFileInfo struct {
@ -78,21 +76,32 @@ type bindataFileInfo struct {
modTime time.Time
}
// Name return file name
func (fi bindataFileInfo) Name() string {
return fi.name
}
// Size return file size
func (fi bindataFileInfo) Size() int64 {
return fi.size
}
// Mode return file mode
func (fi bindataFileInfo) Mode() os.FileMode {
return fi.mode
}
// Mode return file modify time
func (fi bindataFileInfo) ModTime() time.Time {
return fi.modTime
}
// IsDir return file whether a directory
func (fi bindataFileInfo) IsDir() bool {
return false
return fi.mode&os.ModeDir != 0
}
// Sys return file is sys mode
func (fi bindataFileInfo) Sys() interface{} {
return nil
}
@ -112,8 +121,8 @@ func _000001_initDownDbSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "000001_init.down.db.sql", size: 65, mode: os.FileMode(0644), modTime: time.Unix(1607354881, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x5e, 0xbb, 0x3f, 0x1, 0x75, 0x19, 0x70, 0x86, 0xa7, 0x34, 0x40, 0x17, 0x34, 0x3e, 0x18, 0x51, 0x79, 0xd4, 0x22, 0xad, 0x8f, 0x80, 0xcc, 0xa6, 0xcc, 0x6, 0x2b, 0x62, 0x2, 0x47, 0xba, 0xf9}}
info := bindataFileInfo{name: "000001_init.down.db.sql", size: 65, mode: os.FileMode(420), modTime: time.Unix(1581522640, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
@ -132,8 +141,8 @@ func _000001_initUpDbSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "000001_init.up.db.sql", size: 2719, mode: os.FileMode(0644), modTime: time.Unix(1607354881, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x60, 0xdc, 0xeb, 0xe, 0xc2, 0x4f, 0x75, 0xa, 0xf6, 0x3e, 0xc7, 0xc4, 0x4, 0xe2, 0xe1, 0xa4, 0x73, 0x2f, 0x4a, 0xad, 0x1a, 0x0, 0xc3, 0x93, 0x9d, 0x77, 0x3e, 0x31, 0x91, 0x77, 0x2e, 0xc8}}
info := bindataFileInfo{name: "000001_init.up.db.sql", size: 2719, mode: os.FileMode(420), modTime: time.Unix(1581522640, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
@ -152,8 +161,8 @@ func _000002_add_last_ens_clock_valueUpSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "000002_add_last_ens_clock_value.up.sql", size: 77, mode: os.FileMode(0644), modTime: time.Unix(1607354881, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x4d, 0x3, 0x8f, 0xd5, 0x85, 0x83, 0x47, 0xbe, 0xf9, 0x82, 0x7e, 0x81, 0xa4, 0xbd, 0xaa, 0xd5, 0x98, 0x18, 0x5, 0x2d, 0x82, 0x42, 0x3b, 0x3, 0x50, 0xc3, 0x1e, 0x84, 0x35, 0xf, 0xb6, 0x2b}}
info := bindataFileInfo{name: "000002_add_last_ens_clock_value.up.sql", size: 77, mode: os.FileMode(420), modTime: time.Unix(1581522640, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
@ -172,8 +181,8 @@ func _1586358095_add_replaceUpSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1586358095_add_replace.up.sql", size: 224, mode: os.FileMode(0644), modTime: time.Unix(1607354881, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xd2, 0xb3, 0xa9, 0xc7, 0x7f, 0x9d, 0x8f, 0x43, 0x8c, 0x9e, 0x58, 0x8d, 0x44, 0xbc, 0xfa, 0x6b, 0x5f, 0x3f, 0x5a, 0xbe, 0xe8, 0xb1, 0x16, 0xf, 0x91, 0x2a, 0xa0, 0x71, 0xbb, 0x8d, 0x6b, 0xcb}}
info := bindataFileInfo{name: "1586358095_add_replace.up.sql", size: 224, mode: os.FileMode(420), modTime: time.Unix(1587130924, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
@ -192,8 +201,8 @@ func _1588665364_add_image_dataUpSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1588665364_add_image_data.up.sql", size: 186, mode: os.FileMode(0644), modTime: time.Unix(1607354881, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xd6, 0xc6, 0x35, 0xb4, 0x4c, 0x39, 0x96, 0x29, 0x30, 0xda, 0xf4, 0x8f, 0xcb, 0xf1, 0x9f, 0x84, 0xdc, 0x88, 0xd4, 0xd5, 0xbc, 0xb6, 0x5b, 0x46, 0x78, 0x67, 0x76, 0x1a, 0x5, 0x36, 0xdc, 0xe5}}
info := bindataFileInfo{name: "1588665364_add_image_data.up.sql", size: 186, mode: os.FileMode(420), modTime: time.Unix(1599566672, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
@ -212,8 +221,8 @@ func _1589365189_add_pow_targetUpSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1589365189_add_pow_target.up.sql", size: 66, mode: os.FileMode(0644), modTime: time.Unix(1607354881, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x4e, 0x3a, 0xe2, 0x2e, 0x7d, 0xaf, 0xbb, 0xcc, 0x21, 0xa1, 0x7a, 0x41, 0x9a, 0xd0, 0xbb, 0xa9, 0xc8, 0x35, 0xf9, 0x32, 0x34, 0x46, 0x44, 0x9a, 0x86, 0x40, 0x7c, 0xb9, 0x23, 0xc7, 0x3, 0x3f}}
info := bindataFileInfo{name: "1589365189_add_pow_target.up.sql", size: 66, mode: os.FileMode(420), modTime: time.Unix(1599566672, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
@ -232,8 +241,8 @@ func _1591277220_add_index_messagesUpSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1591277220_add_index_messages.up.sql", size: 240, mode: os.FileMode(0644), modTime: time.Unix(1607354881, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x9c, 0xfe, 0xbe, 0xd5, 0xb8, 0x8f, 0xdd, 0xef, 0xbb, 0xa8, 0xad, 0x7f, 0xed, 0x5b, 0x5b, 0x2f, 0xe6, 0x82, 0x27, 0x78, 0x1f, 0xb9, 0x57, 0xdc, 0x8, 0xc2, 0xb2, 0xa9, 0x9a, 0x4, 0xe1, 0x7a}}
info := bindataFileInfo{name: "1591277220_add_index_messages.up.sql", size: 240, mode: os.FileMode(420), modTime: time.Unix(1599566672, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
@ -252,8 +261,8 @@ func _1593087212_add_mute_chat_and_raw_message_fieldsUpSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1593087212_add_mute_chat_and_raw_message_fields.up.sql", size: 215, mode: os.FileMode(0644), modTime: time.Unix(1607354881, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x73, 0x99, 0x61, 0xd1, 0xaa, 0xb4, 0xbf, 0xaf, 0xd7, 0x20, 0x17, 0x40, 0xf9, 0x2, 0xfb, 0xcc, 0x40, 0x2a, 0xd, 0x86, 0x36, 0x30, 0x88, 0x89, 0x25, 0x80, 0x42, 0xb0, 0x5b, 0xe9, 0x73, 0x78}}
info := bindataFileInfo{name: "1593087212_add_mute_chat_and_raw_message_fields.up.sql", size: 215, mode: os.FileMode(420), modTime: time.Unix(1599566672, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
@ -272,8 +281,8 @@ func _1595862781_add_audio_dataUpSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1595862781_add_audio_data.up.sql", size: 246, mode: os.FileMode(0644), modTime: time.Unix(1607354881, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xae, 0xd2, 0xee, 0x55, 0xfb, 0x36, 0xa4, 0x92, 0x66, 0xe, 0x81, 0x62, 0x1e, 0x7a, 0x69, 0xa, 0xd5, 0x4b, 0xa5, 0x6a, 0x8d, 0x1d, 0xce, 0xf3, 0x3e, 0xc0, 0x5f, 0x9c, 0x66, 0x1b, 0xb4, 0xed}}
info := bindataFileInfo{name: "1595862781_add_audio_data.up.sql", size: 246, mode: os.FileMode(420), modTime: time.Unix(1599566672, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
@ -292,8 +301,8 @@ func _1595865249_create_emoji_reactions_tableUpSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1595865249_create_emoji_reactions_table.up.sql", size: 300, mode: os.FileMode(0644), modTime: time.Unix(1607354881, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x3e, 0xc5, 0x43, 0x5c, 0x3d, 0x53, 0x43, 0x2c, 0x1a, 0xa5, 0xb6, 0xbf, 0x7, 0x4, 0x5a, 0x3e, 0x40, 0x8b, 0xa4, 0x57, 0x12, 0x58, 0xbc, 0x42, 0xe2, 0xc3, 0xde, 0x76, 0x98, 0x80, 0xe2, 0xbe}}
info := bindataFileInfo{name: "1595865249_create_emoji_reactions_table.up.sql", size: 300, mode: os.FileMode(420), modTime: time.Unix(1599566672, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
@ -312,8 +321,8 @@ func _1596805115_create_group_chat_invitations_tableUpSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1596805115_create_group_chat_invitations_table.up.sql", size: 231, mode: os.FileMode(0644), modTime: time.Unix(1607354881, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x6d, 0xb1, 0x14, 0x6d, 0x54, 0x28, 0x67, 0xc3, 0x23, 0x6a, 0xfc, 0x80, 0xdf, 0x9e, 0x4c, 0x35, 0x36, 0xf, 0xf8, 0xf3, 0x5f, 0xae, 0xad, 0xb, 0xc1, 0x51, 0x8e, 0x17, 0x7, 0xe5, 0x7f, 0x91}}
info := bindataFileInfo{name: "1596805115_create_group_chat_invitations_table.up.sql", size: 231, mode: os.FileMode(420), modTime: time.Unix(1599566672, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
@ -332,8 +341,8 @@ func _1597322655_add_invitation_admin_chat_fieldUpSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1597322655_add_invitation_admin_chat_field.up.sql", size: 54, mode: os.FileMode(0644), modTime: time.Unix(1607354881, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xa9, 0x7a, 0xa0, 0xf2, 0xdb, 0x13, 0x91, 0x91, 0xa8, 0x34, 0x1a, 0xa1, 0x49, 0x68, 0xd5, 0xae, 0x2c, 0xd8, 0xd5, 0xea, 0x8f, 0x8c, 0xc7, 0x2, 0x4e, 0x58, 0x2c, 0x3a, 0x14, 0xd4, 0x4f, 0x2c}}
info := bindataFileInfo{name: "1597322655_add_invitation_admin_chat_field.up.sql", size: 54, mode: os.FileMode(420), modTime: time.Unix(1599566672, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
@ -352,8 +361,8 @@ func _1597757544_add_nicknameUpSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1597757544_add_nickname.up.sql", size: 52, mode: os.FileMode(0644), modTime: time.Unix(1607354881, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xf4, 0xa2, 0x64, 0x50, 0xc5, 0x4, 0xb9, 0x8b, 0xd1, 0x18, 0x9b, 0xc3, 0x91, 0x36, 0x2a, 0x1f, 0xc3, 0x6c, 0x2d, 0x92, 0xf8, 0x5e, 0xff, 0xb1, 0x59, 0x61, 0x2, 0x1c, 0xe1, 0x85, 0x90, 0xa4}}
info := bindataFileInfo{name: "1597757544_add_nickname.up.sql", size: 52, mode: os.FileMode(420), modTime: time.Unix(1599566672, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
@ -372,8 +381,8 @@ func _1598955122_add_mentionsUpSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1598955122_add_mentions.up.sql", size: 52, mode: os.FileMode(0644), modTime: time.Unix(1607354881, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x8d, 0x22, 0x17, 0x92, 0xd2, 0x11, 0x4e, 0x7, 0x93, 0x9a, 0x55, 0xfd, 0xb, 0x97, 0xc4, 0x63, 0x6a, 0x81, 0x97, 0xcd, 0xb2, 0xf8, 0x4b, 0x5f, 0x3c, 0xfa, 0x3a, 0x38, 0x53, 0x10, 0xed, 0x9d}}
info := bindataFileInfo{name: "1598955122_add_mentions.up.sql", size: 52, mode: os.FileMode(420), modTime: time.Unix(1601295087, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
@ -392,8 +401,8 @@ func _1599641390_add_emoji_reactions_indexUpSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1599641390_add_emoji_reactions_index.up.sql", size: 126, mode: os.FileMode(0644), modTime: time.Unix(1607354881, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xf9, 0xd8, 0xdc, 0xa7, 0xb, 0x92, 0x7a, 0x61, 0x37, 0x24, 0x1c, 0x77, 0x5e, 0xe, 0x7e, 0xfc, 0x9f, 0x98, 0x7b, 0x65, 0xe7, 0xf9, 0x71, 0x57, 0x89, 0x2d, 0x90, 0x1b, 0xf6, 0x5e, 0x37, 0xe8}}
info := bindataFileInfo{name: "1599641390_add_emoji_reactions_index.up.sql", size: 126, mode: os.FileMode(420), modTime: time.Unix(1601295087, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
@ -412,8 +421,8 @@ func _1599720851_add_seen_index_remove_long_messagesUpSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1599720851_add_seen_index_remove_long_messages.up.sql", size: 150, mode: os.FileMode(0644), modTime: time.Unix(1607354881, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x24, 0x1c, 0xc4, 0x78, 0x91, 0xc7, 0xeb, 0xfe, 0xc8, 0xa0, 0xd8, 0x13, 0x27, 0x97, 0xc8, 0x96, 0x56, 0x97, 0x33, 0x2c, 0x1e, 0x16, 0x8a, 0xd3, 0x49, 0x99, 0x3, 0xe9, 0xbb, 0xc4, 0x5, 0x3c}}
info := bindataFileInfo{name: "1599720851_add_seen_index_remove_long_messages.up.sql", size: 150, mode: os.FileMode(420), modTime: time.Unix(1602575653, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
@ -432,8 +441,8 @@ func _1603198582_add_profile_chat_fieldUpSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1603198582_add_profile_chat_field.up.sql", size: 45, mode: os.FileMode(0644), modTime: time.Unix(1607354881, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xaa, 0xca, 0xe, 0x46, 0xa0, 0x9, 0x9d, 0x47, 0x57, 0xe9, 0xfb, 0x17, 0xeb, 0x9c, 0xf6, 0xb8, 0x1d, 0xe9, 0xd, 0x0, 0xd5, 0xe5, 0xd8, 0x9e, 0x60, 0xa, 0xbf, 0x32, 0x2c, 0x52, 0x7f, 0x6a}}
info := bindataFileInfo{name: "1603198582_add_profile_chat_field.up.sql", size: 45, mode: os.FileMode(420), modTime: time.Unix(1614159156, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
@ -452,8 +461,8 @@ func _1603816533_add_linksUpSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1603816533_add_links.up.sql", size: 48, mode: os.FileMode(0644), modTime: time.Unix(1607354881, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xc9, 0x24, 0xd6, 0x1d, 0xa, 0x83, 0x1e, 0x4d, 0xf, 0xae, 0x4d, 0x8c, 0x51, 0x32, 0xa8, 0x37, 0xb0, 0x14, 0xfb, 0x32, 0x34, 0xc8, 0xc, 0x4e, 0x5b, 0xc5, 0x15, 0x65, 0x73, 0x0, 0x0, 0x1d}}
info := bindataFileInfo{name: "1603816533_add_links.up.sql", size: 48, mode: os.FileMode(420), modTime: time.Unix(1614159156, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
@ -472,8 +481,8 @@ func _1603888149_create_chat_identity_last_published_tableUpSql() (*asset, error
return nil, err
}
info := bindataFileInfo{name: "1603888149_create_chat_identity_last_published_table.up.sql", size: 407, mode: os.FileMode(0644), modTime: time.Unix(1608652331, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x7f, 0x9, 0xf, 0xfb, 0xdb, 0x3c, 0x86, 0x70, 0x82, 0xda, 0x10, 0x25, 0xe2, 0x4e, 0x40, 0x45, 0xab, 0x8b, 0x1c, 0x91, 0x7c, 0xf1, 0x70, 0x2e, 0x81, 0xf3, 0x71, 0x45, 0xda, 0xe2, 0xa4, 0x57}}
info := bindataFileInfo{name: "1603888149_create_chat_identity_last_published_table.up.sql", size: 407, mode: os.FileMode(420), modTime: time.Unix(1614159156, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
@ -492,8 +501,8 @@ func _1605075346_add_communitiesUpSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1605075346_add_communities.up.sql", size: 6971, mode: os.FileMode(0644), modTime: time.Unix(1610388501, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x1f, 0x64, 0xea, 0xb4, 0xae, 0x9e, 0xdb, 0x9, 0x58, 0xb6, 0x5c, 0x7a, 0x50, 0xc5, 0xfe, 0x93, 0x5d, 0x36, 0x85, 0x5d, 0x6a, 0xba, 0xc9, 0x7e, 0x84, 0xd7, 0xbf, 0x2a, 0x53, 0xf3, 0x97, 0xf1}}
info := bindataFileInfo{name: "1605075346_add_communities.up.sql", size: 6971, mode: os.FileMode(420), modTime: time.Unix(1614159156, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
@ -512,8 +521,8 @@ func _1610117927_add_message_cacheUpSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1610117927_add_message_cache.up.sql", size: 142, mode: os.FileMode(0644), modTime: time.Unix(1611563400, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x34, 0xf1, 0xf0, 0x82, 0x79, 0x28, 0x19, 0xc2, 0x39, 0x6a, 0xa5, 0x96, 0x59, 0x23, 0xa0, 0xed, 0x60, 0x58, 0x86, 0x9, 0xb9, 0xad, 0xfb, 0xa, 0xe3, 0x47, 0x6e, 0xa1, 0x18, 0xe8, 0x39, 0x2c}}
info := bindataFileInfo{name: "1610117927_add_message_cache.up.sql", size: 142, mode: os.FileMode(420), modTime: time.Unix(1614159156, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
@ -532,8 +541,8 @@ func _1610959908_add_dont_wrap_to_raw_messagesUpSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1610959908_add_dont_wrap_to_raw_messages.up.sql", size: 83, mode: os.FileMode(0644), modTime: time.Unix(1611825589, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x71, 0x2, 0x9a, 0xca, 0xd4, 0x38, 0x44, 0x30, 0x2b, 0xa8, 0x27, 0x32, 0x63, 0x53, 0x22, 0x60, 0x59, 0x84, 0x23, 0x96, 0x77, 0xf0, 0x56, 0xd7, 0x94, 0xe0, 0x95, 0x28, 0x6, 0x1d, 0x4e, 0xb1}}
info := bindataFileInfo{name: "1610959908_add_dont_wrap_to_raw_messages.up.sql", size: 83, mode: os.FileMode(420), modTime: time.Unix(1614159156, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
@ -552,8 +561,8 @@ func _1610960912_add_send_on_personal_topicUpSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1610960912_add_send_on_personal_topic.up.sql", size: 82, mode: os.FileMode(0644), modTime: time.Unix(1611825589, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x77, 0xac, 0x2f, 0xc4, 0xd, 0xa7, 0x1b, 0x37, 0x30, 0xc2, 0x68, 0xee, 0xde, 0x54, 0x5e, 0xbf, 0x3f, 0xa0, 0xd6, 0xc6, 0x9f, 0xd4, 0x34, 0x12, 0x76, 0x1e, 0x66, 0x4a, 0xfc, 0xf, 0xee, 0xc9}}
info := bindataFileInfo{name: "1610960912_add_send_on_personal_topic.up.sql", size: 82, mode: os.FileMode(420), modTime: time.Unix(1614159156, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
@ -572,8 +581,8 @@ func _1612870480_add_datasync_idUpSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1612870480_add_datasync_id.up.sql", size: 111, mode: os.FileMode(0644), modTime: time.Unix(1614251236, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x34, 0x9a, 0xbc, 0xfa, 0xaa, 0x8c, 0x9c, 0x37, 0x67, 0x15, 0x9c, 0x7e, 0x78, 0x75, 0x66, 0x82, 0x18, 0x72, 0x10, 0xbc, 0xd4, 0xab, 0x44, 0xfe, 0x57, 0x85, 0x6d, 0x19, 0xf5, 0x96, 0x8a, 0xbe}}
info := bindataFileInfo{name: "1612870480_add_datasync_id.up.sql", size: 111, mode: os.FileMode(420), modTime: time.Unix(1614159156, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
@ -592,8 +601,8 @@ func _1614152139_add_communities_request_to_joinUpSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1614152139_add_communities_request_to_join.up.sql", size: 831, mode: os.FileMode(0644), modTime: time.Unix(1614608661, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x11, 0x3, 0x26, 0xf9, 0x29, 0x50, 0x4f, 0xcd, 0x46, 0xe5, 0xb1, 0x6b, 0xb9, 0x2, 0x40, 0xb1, 0xdf, 0x4a, 0x4c, 0x7a, 0xda, 0x3, 0x35, 0xcd, 0x2d, 0xcc, 0x80, 0x7d, 0x57, 0x5f, 0x3, 0x5c}}
info := bindataFileInfo{name: "1614152139_add_communities_request_to_join.up.sql", size: 831, mode: os.FileMode(420), modTime: time.Unix(1616621584, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
@ -612,8 +621,8 @@ func _1615374373_add_confirmationsUpSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1615374373_add_confirmations.up.sql", size: 227, mode: os.FileMode(0644), modTime: time.Unix(1615904318, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xdd, 0xa6, 0x65, 0xc5, 0x1d, 0xb2, 0x77, 0x36, 0xe3, 0x79, 0xda, 0xe8, 0x7a, 0xa4, 0xdf, 0x45, 0xae, 0xd8, 0xb4, 0xba, 0x90, 0xfd, 0x74, 0x71, 0x14, 0x75, 0x73, 0x72, 0xb9, 0x9e, 0x1, 0x81}}
info := bindataFileInfo{name: "1615374373_add_confirmations.up.sql", size: 227, mode: os.FileMode(420), modTime: time.Unix(1616621584, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
@ -632,8 +641,28 @@ func _1617694931_add_notification_centerUpSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1617694931_add_notification_center.up.sql", size: 572, mode: os.FileMode(0644), modTime: time.Unix(1618824585, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x10, 0x45, 0xc6, 0xc9, 0x73, 0xbb, 0x1f, 0xda, 0xa3, 0x4d, 0x19, 0x98, 0x85, 0x2d, 0xca, 0xda, 0xcc, 0x3b, 0x32, 0xff, 0xc7, 0x7b, 0xe3, 0x9f, 0x9b, 0x2a, 0x93, 0xf5, 0xdf, 0x65, 0x38, 0x91}}
info := bindataFileInfo{name: "1617694931_add_notification_center.up.sql", size: 572, mode: os.FileMode(420), modTime: time.Unix(1618923733, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
var __1618923660_create_pin_messagesUpSql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x7c\xcc\x41\x0b\xc2\x20\x18\xc6\xf1\xbb\x9f\xe2\x3d\x16\xf4\x0d\x3a\xb9\xf1\x56\x92\xb9\x70\x16\xed\x24\xe2\xa4\x49\x6e\x93\x5c\xf5\xf5\xa3\x88\xd8\x0e\x75\xfe\xff\x9e\x27\x97\x48\x15\x82\xa2\x19\x47\x60\x2b\x10\x85\x02\x3c\xb1\x52\x95\x10\x7d\xa7\x5b\x97\x92\x39\xbb\x04\x33\x02\xe0\x6b\x38\x52\x99\x6f\xa8\x84\xbd\x64\x3b\x2a\x2b\xd8\x62\xf5\x9e\x88\x03\xe7\x0b\x02\xf0\xf1\x7a\x44\xc7\xf9\xd1\xf8\x14\xdd\x55\x0f\xbe\x75\x69\x30\x6d\x04\x26\x14\xae\x71\xaa\x6c\x63\x86\x5f\x0f\xa1\xb7\x26\xe8\x7f\xc2\x86\xde\x5e\xf4\xdd\x84\x9b\x7b\xbd\x4f\x5a\xf4\x5d\xe7\x6a\xc8\x8a\x82\x23\x15\xdf\x44\xe6\x4b\xf2\x0c\x00\x00\xff\xff\x44\xad\x25\xa3\x09\x01\x00\x00")
func _1618923660_create_pin_messagesUpSqlBytes() ([]byte, error) {
return bindataRead(
__1618923660_create_pin_messagesUpSql,
"1618923660_create_pin_messages.up.sql",
)
}
func _1618923660_create_pin_messagesUpSql() (*asset, error) {
bytes, err := _1618923660_create_pin_messagesUpSqlBytes()
if err != nil {
return nil, err
}
info := bindataFileInfo{name: "1618923660_create_pin_messages.up.sql", size: 265, mode: os.FileMode(420), modTime: time.Unix(1620337259, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
@ -652,8 +681,8 @@ func readmeMd() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "README.md", size: 554, mode: os.FileMode(0644), modTime: time.Unix(1610388501, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x1c, 0x6e, 0xfb, 0xcc, 0x81, 0x94, 0x4d, 0x8c, 0xa0, 0x3b, 0x5, 0xb0, 0x18, 0xd6, 0xbb, 0xb3, 0x79, 0xc8, 0x8f, 0xff, 0xc1, 0x10, 0xf9, 0xf, 0x20, 0x1b, 0x4a, 0x74, 0x96, 0x42, 0xd7, 0xa8}}
info := bindataFileInfo{name: "README.md", size: 554, mode: os.FileMode(420), modTime: time.Unix(1614159156, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
@ -672,8 +701,8 @@ func docGo() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "doc.go", size: 850, mode: os.FileMode(0644), modTime: time.Unix(1607354881, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xa0, 0xcc, 0x41, 0xe1, 0x61, 0x12, 0x97, 0xe, 0x36, 0x8c, 0xa7, 0x9e, 0xe0, 0x6e, 0x59, 0x9e, 0xee, 0xd5, 0x4a, 0xcf, 0x1e, 0x60, 0xd6, 0xc3, 0x3a, 0xc9, 0x6c, 0xf2, 0x86, 0x5a, 0xb4, 0x1e}}
info := bindataFileInfo{name: "doc.go", size: 850, mode: os.FileMode(420), modTime: time.Unix(1620338680, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
@ -681,8 +710,8 @@ func docGo() (*asset, error) {
// It returns an error if the asset could not be found or
// could not be loaded.
func Asset(name string) ([]byte, error) {
canonicalName := strings.Replace(name, "\\", "/", -1)
if f, ok := _bindata[canonicalName]; ok {
cannonicalName := strings.Replace(name, "\\", "/", -1)
if f, ok := _bindata[cannonicalName]; ok {
a, err := f()
if err != nil {
return nil, fmt.Errorf("Asset %s can't read by error: %v", name, err)
@ -692,12 +721,6 @@ func Asset(name string) ([]byte, error) {
return nil, fmt.Errorf("Asset %s not found", name)
}
// AssetString returns the asset contents as a string (instead of a []byte).
func AssetString(name string) (string, error) {
data, err := Asset(name)
return string(data), err
}
// MustAsset is like Asset but panics when Asset would return an error.
// It simplifies safe initialization of global variables.
func MustAsset(name string) []byte {
@ -709,18 +732,12 @@ func MustAsset(name string) []byte {
return a
}
// MustAssetString is like AssetString but panics when Asset would return an
// error. It simplifies safe initialization of global variables.
func MustAssetString(name string) string {
return string(MustAsset(name))
}
// AssetInfo loads and returns the asset info for the given name.
// It returns an error if the asset could not be found or
// could not be loaded.
func AssetInfo(name string) (os.FileInfo, error) {
canonicalName := strings.Replace(name, "\\", "/", -1)
if f, ok := _bindata[canonicalName]; ok {
cannonicalName := strings.Replace(name, "\\", "/", -1)
if f, ok := _bindata[cannonicalName]; ok {
a, err := f()
if err != nil {
return nil, fmt.Errorf("AssetInfo %s can't read by error: %v", name, err)
@ -730,33 +747,6 @@ func AssetInfo(name string) (os.FileInfo, error) {
return nil, fmt.Errorf("AssetInfo %s not found", name)
}
// AssetDigest returns the digest of the file with the given name. It returns an
// error if the asset could not be found or the digest could not be loaded.
func AssetDigest(name string) ([sha256.Size]byte, error) {
canonicalName := strings.Replace(name, "\\", "/", -1)
if f, ok := _bindata[canonicalName]; ok {
a, err := f()
if err != nil {
return [sha256.Size]byte{}, fmt.Errorf("AssetDigest %s can't read by error: %v", name, err)
}
return a.digest, nil
}
return [sha256.Size]byte{}, fmt.Errorf("AssetDigest %s not found", name)
}
// Digests returns a map of all known files and their checksums.
func Digests() (map[string][sha256.Size]byte, error) {
mp := make(map[string][sha256.Size]byte, len(_bindata))
for name := range _bindata {
a, err := _bindata[name]()
if err != nil {
return nil, err
}
mp[name] = a.digest
}
return mp, nil
}
// AssetNames returns the names of the assets.
func AssetNames() []string {
names := make([]string, 0, len(_bindata))
@ -768,63 +758,36 @@ func AssetNames() []string {
// _bindata is a table, holding each asset generator, mapped to its name.
var _bindata = map[string]func() (*asset, error){
"000001_init.down.db.sql": _000001_initDownDbSql,
"000001_init.up.db.sql": _000001_initUpDbSql,
"000002_add_last_ens_clock_value.up.sql": _000002_add_last_ens_clock_valueUpSql,
"1586358095_add_replace.up.sql": _1586358095_add_replaceUpSql,
"1588665364_add_image_data.up.sql": _1588665364_add_image_dataUpSql,
"1589365189_add_pow_target.up.sql": _1589365189_add_pow_targetUpSql,
"1591277220_add_index_messages.up.sql": _1591277220_add_index_messagesUpSql,
"1593087212_add_mute_chat_and_raw_message_fields.up.sql": _1593087212_add_mute_chat_and_raw_message_fieldsUpSql,
"1595862781_add_audio_data.up.sql": _1595862781_add_audio_dataUpSql,
"1595865249_create_emoji_reactions_table.up.sql": _1595865249_create_emoji_reactions_tableUpSql,
"1596805115_create_group_chat_invitations_table.up.sql": _1596805115_create_group_chat_invitations_tableUpSql,
"1597322655_add_invitation_admin_chat_field.up.sql": _1597322655_add_invitation_admin_chat_fieldUpSql,
"1597757544_add_nickname.up.sql": _1597757544_add_nicknameUpSql,
"1598955122_add_mentions.up.sql": _1598955122_add_mentionsUpSql,
"1599641390_add_emoji_reactions_index.up.sql": _1599641390_add_emoji_reactions_indexUpSql,
"1599720851_add_seen_index_remove_long_messages.up.sql": _1599720851_add_seen_index_remove_long_messagesUpSql,
"1603198582_add_profile_chat_field.up.sql": _1603198582_add_profile_chat_fieldUpSql,
"1603816533_add_links.up.sql": _1603816533_add_linksUpSql,
"000001_init.down.db.sql": _000001_initDownDbSql,
"000001_init.up.db.sql": _000001_initUpDbSql,
"000002_add_last_ens_clock_value.up.sql": _000002_add_last_ens_clock_valueUpSql,
"1586358095_add_replace.up.sql": _1586358095_add_replaceUpSql,
"1588665364_add_image_data.up.sql": _1588665364_add_image_dataUpSql,
"1589365189_add_pow_target.up.sql": _1589365189_add_pow_targetUpSql,
"1591277220_add_index_messages.up.sql": _1591277220_add_index_messagesUpSql,
"1593087212_add_mute_chat_and_raw_message_fields.up.sql": _1593087212_add_mute_chat_and_raw_message_fieldsUpSql,
"1595862781_add_audio_data.up.sql": _1595862781_add_audio_dataUpSql,
"1595865249_create_emoji_reactions_table.up.sql": _1595865249_create_emoji_reactions_tableUpSql,
"1596805115_create_group_chat_invitations_table.up.sql": _1596805115_create_group_chat_invitations_tableUpSql,
"1597322655_add_invitation_admin_chat_field.up.sql": _1597322655_add_invitation_admin_chat_fieldUpSql,
"1597757544_add_nickname.up.sql": _1597757544_add_nicknameUpSql,
"1598955122_add_mentions.up.sql": _1598955122_add_mentionsUpSql,
"1599641390_add_emoji_reactions_index.up.sql": _1599641390_add_emoji_reactions_indexUpSql,
"1599720851_add_seen_index_remove_long_messages.up.sql": _1599720851_add_seen_index_remove_long_messagesUpSql,
"1603198582_add_profile_chat_field.up.sql": _1603198582_add_profile_chat_fieldUpSql,
"1603816533_add_links.up.sql": _1603816533_add_linksUpSql,
"1603888149_create_chat_identity_last_published_table.up.sql": _1603888149_create_chat_identity_last_published_tableUpSql,
"1605075346_add_communities.up.sql": _1605075346_add_communitiesUpSql,
"1610117927_add_message_cache.up.sql": _1610117927_add_message_cacheUpSql,
"1610959908_add_dont_wrap_to_raw_messages.up.sql": _1610959908_add_dont_wrap_to_raw_messagesUpSql,
"1610960912_add_send_on_personal_topic.up.sql": _1610960912_add_send_on_personal_topicUpSql,
"1612870480_add_datasync_id.up.sql": _1612870480_add_datasync_idUpSql,
"1614152139_add_communities_request_to_join.up.sql": _1614152139_add_communities_request_to_joinUpSql,
"1615374373_add_confirmations.up.sql": _1615374373_add_confirmationsUpSql,
"1617694931_add_notification_center.up.sql": _1617694931_add_notification_centerUpSql,
"README.md": readmeMd,
"doc.go": docGo,
"1605075346_add_communities.up.sql": _1605075346_add_communitiesUpSql,
"1610117927_add_message_cache.up.sql": _1610117927_add_message_cacheUpSql,
"1610959908_add_dont_wrap_to_raw_messages.up.sql": _1610959908_add_dont_wrap_to_raw_messagesUpSql,
"1610960912_add_send_on_personal_topic.up.sql": _1610960912_add_send_on_personal_topicUpSql,
"1612870480_add_datasync_id.up.sql": _1612870480_add_datasync_idUpSql,
"1614152139_add_communities_request_to_join.up.sql": _1614152139_add_communities_request_to_joinUpSql,
"1615374373_add_confirmations.up.sql": _1615374373_add_confirmationsUpSql,
"1617694931_add_notification_center.up.sql": _1617694931_add_notification_centerUpSql,
"1618923660_create_pin_messages.up.sql": _1618923660_create_pin_messagesUpSql,
"README.md": readmeMd,
"doc.go": docGo,
}
// AssetDir returns the file names below a certain
@ -836,15 +799,15 @@ var _bindata = map[string]func() (*asset, error){
// img/
// a.png
// b.png
// then AssetDir("data") would return []string{"foo.txt", "img"},
// AssetDir("data/img") would return []string{"a.png", "b.png"},
// AssetDir("foo.txt") and AssetDir("notexist") would return an error, and
// then AssetDir("data") would return []string{"foo.txt", "img"}
// AssetDir("data/img") would return []string{"a.png", "b.png"}
// AssetDir("foo.txt") and AssetDir("notexist") would return an error
// AssetDir("") will return []string{"data"}.
func AssetDir(name string) ([]string, error) {
node := _bintree
if len(name) != 0 {
canonicalName := strings.Replace(name, "\\", "/", -1)
pathList := strings.Split(canonicalName, "/")
cannonicalName := strings.Replace(name, "\\", "/", -1)
pathList := strings.Split(cannonicalName, "/")
for _, p := range pathList {
node = node.Children[p]
if node == nil {
@ -895,11 +858,12 @@ var _bintree = &bintree{nil, map[string]*bintree{
"1614152139_add_communities_request_to_join.up.sql": &bintree{_1614152139_add_communities_request_to_joinUpSql, map[string]*bintree{}},
"1615374373_add_confirmations.up.sql": &bintree{_1615374373_add_confirmationsUpSql, map[string]*bintree{}},
"1617694931_add_notification_center.up.sql": &bintree{_1617694931_add_notification_centerUpSql, map[string]*bintree{}},
"README.md": &bintree{readmeMd, map[string]*bintree{}},
"doc.go": &bintree{docGo, map[string]*bintree{}},
"1618923660_create_pin_messages.up.sql": &bintree{_1618923660_create_pin_messagesUpSql, map[string]*bintree{}},
"README.md": &bintree{readmeMd, map[string]*bintree{}},
"doc.go": &bintree{docGo, map[string]*bintree{}},
}}
// RestoreAsset restores an asset under the given directory.
// RestoreAsset restores an asset under the given directory
func RestoreAsset(dir, name string) error {
data, err := Asset(name)
if err != nil {
@ -917,10 +881,14 @@ func RestoreAsset(dir, name string) error {
if err != nil {
return err
}
return os.Chtimes(_filePath(dir, name), info.ModTime(), info.ModTime())
err = os.Chtimes(_filePath(dir, name), info.ModTime(), info.ModTime())
if err != nil {
return err
}
return nil
}
// RestoreAssets restores an asset under the given directory recursively.
// RestoreAssets restores an asset under the given directory recursively
func RestoreAssets(dir, name string) error {
children, err := AssetDir(name)
// File
@ -938,6 +906,6 @@ func RestoreAssets(dir, name string) error {
}
func _filePath(dir, name string) string {
canonicalName := strings.Replace(name, "\\", "/", -1)
return filepath.Join(append([]string{dir}, strings.Split(canonicalName, "/")...)...)
cannonicalName := strings.Replace(name, "\\", "/", -1)
return filepath.Join(append([]string{dir}, strings.Split(cannonicalName, "/")...)...)
}

View File

@ -0,0 +1,9 @@
CREATE TABLE IF NOT EXISTS pin_messages (
id VARCHAR PRIMARY KEY NOT NULL,
message_id VARCHAR NOT NULL,
whisper_timestamp INTEGER NOT NULL,
chat_id VARCHAR NOT NULL,
local_chat_id VARCHAR NOT NULL,
clock_value INT NOT NULL,
pinned BOOLEAN NOT NULL
);

View File

@ -181,6 +181,119 @@ func TestMessageByChatID(t *testing.T) {
)
}
func TestPinMessageByChatID(t *testing.T) {
db, err := openTestDB()
require.NoError(t, err)
p := sqlitePersistence{db: db}
chatID := "chat-with-pinned-messages"
messagesCount := 1000
pageSize := 5
pinnedMessagesCount := 0
var messages []*common.Message
var pinMessages []*common.PinMessage
for i := 0; i < messagesCount; i++ {
messages = append(messages, &common.Message{
ID: strconv.Itoa(i),
LocalChatID: chatID,
ChatMessage: protobuf.ChatMessage{
Clock: uint64(i),
},
From: "me",
})
// Pin this message
if i%100 == 0 {
pinMessage := &common.PinMessage{
ID: strconv.Itoa(i),
LocalChatID: chatID,
From: "me",
}
pinMessage.MessageId = strconv.Itoa(i)
pinMessage.Clock = 111
pinMessage.Pinned = true
pinMessages = append(pinMessages, pinMessage)
pinnedMessagesCount++
if i%200 == 0 {
// unpin a message
unpinMessage := &common.PinMessage{
ID: strconv.Itoa(i),
LocalChatID: chatID,
From: "me",
}
pinMessage.MessageId = strconv.Itoa(i)
unpinMessage.Clock = 333
unpinMessage.Pinned = false
pinMessages = append(pinMessages, unpinMessage)
pinnedMessagesCount--
// pinned before the unpin
pinMessage2 := &common.PinMessage{
ID: strconv.Itoa(i),
LocalChatID: chatID,
From: "me",
}
pinMessage2.MessageId = strconv.Itoa(i)
pinMessage2.Clock = 222
pinMessage2.Pinned = true
pinMessages = append(pinMessages, pinMessage2)
}
}
// Add some other chats.
if i%5 == 0 {
messages = append(messages, &common.Message{
ID: strconv.Itoa(messagesCount + i),
LocalChatID: "chat-without-pinned-messages",
ChatMessage: protobuf.ChatMessage{
Clock: uint64(i),
},
From: "me",
})
}
}
err = p.SaveMessages(messages)
require.NoError(t, err)
err = p.SavePinMessages(pinMessages)
require.NoError(t, err)
var (
result []*common.PinnedMessage
cursor string
iter int
)
for {
var (
items []*common.PinnedMessage
err error
)
items, cursor, err = p.PinnedMessageByChatID(chatID, cursor, pageSize)
require.NoError(t, err)
result = append(result, items...)
iter++
if len(cursor) == 0 || iter > messagesCount {
break
}
}
require.Equal(t, "", cursor) // for loop should exit because of cursor being empty
require.EqualValues(t, pinnedMessagesCount, len(result))
require.EqualValues(t, math.Ceil(float64(pinnedMessagesCount)/float64(pageSize)), iter)
require.True(
t,
// Verify descending order.
sort.SliceIsSorted(result, func(i, j int) bool {
return result[i].Clock > result[j].Clock
}),
)
}
func TestMessageReplies(t *testing.T) {
db, err := openTestDB()
require.NoError(t, err)

View File

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

View File

@ -40,5 +40,6 @@ message ApplicationMetadataMessage {
COMMUNITY_DESCRIPTION = 25;
COMMUNITY_INVITATION = 26;
COMMUNITY_REQUEST_TO_JOIN = 27;
PIN_MESSAGE = 28;
}
}

View File

@ -0,0 +1,118 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// source: pin_message.proto
package protobuf
import (
fmt "fmt"
math "math"
proto "github.com/golang/protobuf/proto"
)
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
// This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
type PinMessage struct {
Clock uint64 `protobuf:"varint,1,opt,name=clock,proto3" json:"clock,omitempty"`
MessageId string `protobuf:"bytes,2,opt,name=message_id,json=messageId,proto3" json:"message_id,omitempty"`
ChatId string `protobuf:"bytes,3,opt,name=chat_id,json=chatId,proto3" json:"chat_id,omitempty"`
Pinned bool `protobuf:"varint,4,opt,name=pinned,proto3" json:"pinned,omitempty"`
// The type of message (public/one-to-one/private-group-chat)
MessageType MessageType `protobuf:"varint,5,opt,name=message_type,json=messageType,proto3,enum=protobuf.MessageType" json:"message_type,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *PinMessage) Reset() { *m = PinMessage{} }
func (m *PinMessage) String() string { return proto.CompactTextString(m) }
func (*PinMessage) ProtoMessage() {}
func (*PinMessage) Descriptor() ([]byte, []int) {
return fileDescriptor_b3c2ad1be7128a0a, []int{0}
}
func (m *PinMessage) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_PinMessage.Unmarshal(m, b)
}
func (m *PinMessage) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_PinMessage.Marshal(b, m, deterministic)
}
func (m *PinMessage) XXX_Merge(src proto.Message) {
xxx_messageInfo_PinMessage.Merge(m, src)
}
func (m *PinMessage) XXX_Size() int {
return xxx_messageInfo_PinMessage.Size(m)
}
func (m *PinMessage) XXX_DiscardUnknown() {
xxx_messageInfo_PinMessage.DiscardUnknown(m)
}
var xxx_messageInfo_PinMessage proto.InternalMessageInfo
func (m *PinMessage) GetClock() uint64 {
if m != nil {
return m.Clock
}
return 0
}
func (m *PinMessage) GetMessageId() string {
if m != nil {
return m.MessageId
}
return ""
}
func (m *PinMessage) GetChatId() string {
if m != nil {
return m.ChatId
}
return ""
}
func (m *PinMessage) GetPinned() bool {
if m != nil {
return m.Pinned
}
return false
}
func (m *PinMessage) GetMessageType() MessageType {
if m != nil {
return m.MessageType
}
return MessageType_UNKNOWN_MESSAGE_TYPE
}
func init() {
proto.RegisterType((*PinMessage)(nil), "protobuf.PinMessage")
}
func init() {
proto.RegisterFile("pin_message.proto", fileDescriptor_b3c2ad1be7128a0a)
}
var fileDescriptor_b3c2ad1be7128a0a = []byte{
// 183 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x12, 0x2c, 0xc8, 0xcc, 0x8b,
0xcf, 0x4d, 0x2d, 0x2e, 0x4e, 0x4c, 0x4f, 0xd5, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0xe2, 0x00,
0x53, 0x49, 0xa5, 0x69, 0x52, 0xdc, 0xa9, 0x79, 0xa5, 0xb9, 0xc5, 0x10, 0x61, 0xa5, 0x35, 0x8c,
0x5c, 0x5c, 0x01, 0x99, 0x79, 0xbe, 0x10, 0xb5, 0x42, 0x22, 0x5c, 0xac, 0xc9, 0x39, 0xf9, 0xc9,
0xd9, 0x12, 0x8c, 0x0a, 0x8c, 0x1a, 0x2c, 0x41, 0x10, 0x8e, 0x90, 0x2c, 0x17, 0x17, 0xd4, 0xb0,
0xf8, 0xcc, 0x14, 0x09, 0x26, 0x05, 0x46, 0x0d, 0xce, 0x20, 0x4e, 0xa8, 0x88, 0x67, 0x8a, 0x90,
0x38, 0x17, 0x7b, 0x72, 0x46, 0x62, 0x09, 0x48, 0x8e, 0x19, 0x2c, 0xc7, 0x06, 0xe2, 0x7a, 0xa6,
0x08, 0x89, 0x71, 0xb1, 0x15, 0x64, 0xe6, 0xe5, 0xa5, 0xa6, 0x48, 0xb0, 0x28, 0x30, 0x6a, 0x70,
0x04, 0x41, 0x79, 0x42, 0x16, 0x5c, 0x3c, 0x30, 0xf3, 0x4a, 0x2a, 0x0b, 0x52, 0x25, 0x58, 0x15,
0x18, 0x35, 0xf8, 0x8c, 0x44, 0xf5, 0x60, 0x4e, 0xd4, 0x83, 0x3a, 0x27, 0xa4, 0xb2, 0x20, 0x35,
0x88, 0x3b, 0x17, 0xc1, 0x49, 0x62, 0x03, 0x2b, 0x31, 0x06, 0x04, 0x00, 0x00, 0xff, 0xff, 0x3d,
0x9f, 0x5d, 0x9f, 0xe1, 0x00, 0x00, 0x00,
}

View File

@ -0,0 +1,14 @@
syntax = "proto3";
package protobuf;
import "enums.proto";
message PinMessage {
uint64 clock = 1;
string message_id = 2;
string chat_id = 3;
bool pinned = 4;
// The type of message (public/one-to-one/private-group-chat)
MessageType message_type = 5;
}

View File

@ -190,6 +190,9 @@ func (m *StatusMessage) HandleApplication() error {
case protobuf.ApplicationMetadataMessage_CONTACT_UPDATE:
return m.unmarshalProtobufData(new(protobuf.ContactUpdate))
case protobuf.ApplicationMetadataMessage_PIN_MESSAGE:
return m.unmarshalProtobufData(new(protobuf.PinMessage))
case protobuf.ApplicationMetadataMessage_SYNC_INSTALLATION:
return m.unmarshalProtobufData(new(protobuf.SyncInstallation))

View File

@ -423,6 +423,11 @@ type ApplicationMessagesResponse struct {
Cursor string `json:"cursor"`
}
type ApplicationPinnedMessagesResponse struct {
Messages []*common.PinnedMessage `json:"messages"`
Cursor string `json:"cursor"`
}
func (api *PublicAPI) ChatMessages(chatID, cursor string, limit int) (*ApplicationMessagesResponse, error) {
messages, cursor, err := api.service.messenger.MessageByChatID(chatID, cursor, limit)
if err != nil {
@ -435,6 +440,18 @@ func (api *PublicAPI) ChatMessages(chatID, cursor string, limit int) (*Applicati
}, nil
}
func (api *PublicAPI) ChatPinnedMessages(chatID, cursor string, limit int) (*ApplicationPinnedMessagesResponse, error) {
messages, cursor, err := api.service.messenger.PinnedMessageByChatID(chatID, cursor, limit)
if err != nil {
return nil, err
}
return &ApplicationPinnedMessagesResponse{
Messages: messages,
Cursor: cursor,
}, nil
}
func (api *PublicAPI) StartMessenger() (*protocol.MessengerResponse, error) {
return api.service.StartMessenger()
}
@ -487,6 +504,10 @@ func (api *PublicAPI) SendChatMessages(ctx context.Context, messages []*common.M
return api.service.messenger.SendChatMessages(ctx, messages)
}
func (api *PublicAPI) SendPinMessage(ctx context.Context, message *common.PinMessage) (*protocol.MessengerResponse, error) {
return api.service.messenger.SendPinMessage(ctx, message)
}
func (api *PublicAPI) RequestTransaction(ctx context.Context, chatID, value, contract, address string) (*protocol.MessengerResponse, error) {
return api.service.messenger.RequestTransaction(ctx, chatID, value, contract, address)
}

View File

@ -507,6 +507,10 @@ func (api *NimbusPublicAPI) SendChatMessage(ctx context.Context, message *common
return api.service.messenger.SendChatMessage(ctx, message)
}
func (api *NimbusPublicAPI) SendPinMessage(ctx context.Context, message *common.PinMessage) (*protocol.MessengerResponse, error) {
return api.service.messenger.SendPinMessage(ctx, message)
}
func (api *NimbusPublicAPI) ReSendChatMessage(ctx context.Context, messageID string) error {
return api.service.messenger.ReSendChatMessage(ctx, messageID)
}