Add chats API calls
This commits adds two API calls: SaveChat GetChats The first one behaves like an upsert. The second takes two parameters, from, to and returns chats in reverse order by timestamp. Members and MembershipUpdates have been stored as blob of data to simplify the querying (we never query them separately).
This commit is contained in:
parent
f102941b62
commit
b742356b68
80
chat.go
80
chat.go
|
@ -2,8 +2,80 @@ package statusproto
|
|||
|
||||
import "crypto/ecdsa"
|
||||
|
||||
type Chat interface {
|
||||
ID() string
|
||||
PublicName() string
|
||||
PublicKey() *ecdsa.PublicKey
|
||||
type ChatPagination struct {
|
||||
From uint
|
||||
To uint
|
||||
}
|
||||
|
||||
type ChatType int
|
||||
|
||||
const (
|
||||
ChatTypeOneToOne = iota + 1
|
||||
ChatTypePublic
|
||||
ChatTypePrivateGroupChat
|
||||
)
|
||||
|
||||
type Chat struct {
|
||||
// ID is the id of the chat, for public chats it is the name e.g. status, for one-to-one
|
||||
// is the hex encoded public key and for group chats is a random uuid appended with
|
||||
// the hex encoded pk of the creator of the chat
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Color string `json:"color"`
|
||||
// Active indicates whether the chat has been soft deleted
|
||||
Active bool `json:"active"`
|
||||
|
||||
ChatType ChatType `json:"chatType"`
|
||||
|
||||
// Only filled for one to one chats
|
||||
PublicKey *ecdsa.PublicKey `json:"-"`
|
||||
|
||||
// Timestamp indicates the last time this chat has received/sent a message
|
||||
Timestamp int64 `json:"timestamp"`
|
||||
// LastClockValue indicates the last clock value to be used when sending messages
|
||||
LastClockValue uint64 `json:"lastClockValue"`
|
||||
// DeletedAtClockValue indicates the clock value at time of deletion, messages
|
||||
// with lower clock value of this should be discarded
|
||||
DeletedAtClockValue uint64 `json:"deletedAtClockValue"`
|
||||
|
||||
// Denormalized fields
|
||||
UnviewedMessagesCount uint `json:"unviewedMessagesCount"`
|
||||
LastMessageContentType string `json:"lastMessageContentType"`
|
||||
LastMessageContent string `json:"lastMessageContent"`
|
||||
|
||||
// Group chat fields
|
||||
// Members are the members who have been invited to the group chat
|
||||
Members []ChatMember `json:"members"`
|
||||
// MembershipUpdates is all the membership events in the chat
|
||||
MembershipUpdates []ChatMembershipUpdate `json:"membershipUpdates"`
|
||||
}
|
||||
|
||||
// ChatMembershipUpdate represent an event on membership of the chat
|
||||
type ChatMembershipUpdate struct {
|
||||
// Unique identifier for the event
|
||||
ID string `json:"id"`
|
||||
// Type indicates the kind of event (i.e changed-name, added-member, etc)
|
||||
Type string `json:"type"`
|
||||
// Name represents the name in the event of changing name events
|
||||
Name string `json:"name"`
|
||||
// Clock value of the event
|
||||
ClockValue uint64 `json:"clockValue"`
|
||||
// Signature of the event
|
||||
Signature string `json:"signature"`
|
||||
// Hex encoded public key of the creator of the event
|
||||
From string `json:"from"`
|
||||
// Target of the event for single-target events
|
||||
Member string `json:"member"`
|
||||
// Target of the event for multi-target events
|
||||
Members []string `json:"members"`
|
||||
}
|
||||
|
||||
// ChatMember represents a member who participates in a group chat
|
||||
type ChatMember struct {
|
||||
// ID is the hex encoded public key of the member
|
||||
ID string `json:"id"`
|
||||
// Admin indicates if the member is an admin of the group chat
|
||||
Admin bool `json:"admin"`
|
||||
// Joined indicates if the member has joined the group chat
|
||||
Joined bool `json:"joined"`
|
||||
}
|
||||
|
|
|
@ -100,7 +100,7 @@ func _1536754952_initial_schemaDownSql() (*asset, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "1536754952_initial_schema.down.sql", size: 83, mode: os.FileMode(0644), modTime: time.Unix(1564400388, 0)}
|
||||
info := bindataFileInfo{name: "1536754952_initial_schema.down.sql", size: 83, mode: os.FileMode(0644), modTime: time.Unix(1564484687, 0)}
|
||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x44, 0xcf, 0x76, 0x71, 0x1f, 0x5e, 0x9a, 0x43, 0xd8, 0xcd, 0xb8, 0xc3, 0x70, 0xc3, 0x7f, 0xfc, 0x90, 0xb4, 0x25, 0x1e, 0xf4, 0x66, 0x20, 0xb8, 0x33, 0x7e, 0xb0, 0x76, 0x1f, 0xc, 0xc0, 0x75}}
|
||||
return a, nil
|
||||
}
|
||||
|
@ -120,7 +120,7 @@ func _1536754952_initial_schemaUpSql() (*asset, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "1536754952_initial_schema.up.sql", size: 962, mode: os.FileMode(0644), modTime: time.Unix(1564400388, 0)}
|
||||
info := bindataFileInfo{name: "1536754952_initial_schema.up.sql", size: 962, mode: os.FileMode(0644), modTime: time.Unix(1564484687, 0)}
|
||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xea, 0x90, 0x5a, 0x59, 0x3e, 0x3, 0xe2, 0x3c, 0x81, 0x42, 0xcd, 0x4c, 0x9a, 0xe8, 0xda, 0x93, 0x2b, 0x70, 0xa4, 0xd5, 0x29, 0x3e, 0xd5, 0xc9, 0x27, 0xb6, 0xb7, 0x65, 0xff, 0x0, 0xcb, 0xde}}
|
||||
return a, nil
|
||||
}
|
||||
|
@ -140,7 +140,7 @@ func _1539249977_update_ratchet_infoDownSql() (*asset, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "1539249977_update_ratchet_info.down.sql", size: 311, mode: os.FileMode(0644), modTime: time.Unix(1564400388, 0)}
|
||||
info := bindataFileInfo{name: "1539249977_update_ratchet_info.down.sql", size: 311, mode: os.FileMode(0644), modTime: time.Unix(1564484687, 0)}
|
||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x1, 0xa4, 0xeb, 0xa0, 0xe6, 0xa0, 0xd4, 0x48, 0xbb, 0xad, 0x6f, 0x7d, 0x67, 0x8c, 0xbd, 0x25, 0xde, 0x1f, 0x73, 0x9a, 0xbb, 0xa8, 0xc9, 0x30, 0xb7, 0xa9, 0x7c, 0xaf, 0xb5, 0x1, 0x61, 0xdd}}
|
||||
return a, nil
|
||||
}
|
||||
|
@ -160,7 +160,7 @@ func _1539249977_update_ratchet_infoUpSql() (*asset, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "1539249977_update_ratchet_info.up.sql", size: 368, mode: os.FileMode(0644), modTime: time.Unix(1564400388, 0)}
|
||||
info := bindataFileInfo{name: "1539249977_update_ratchet_info.up.sql", size: 368, mode: os.FileMode(0644), modTime: time.Unix(1564484687, 0)}
|
||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xc, 0x8e, 0xbf, 0x6f, 0xa, 0xc0, 0xe1, 0x3c, 0x42, 0x28, 0x88, 0x1d, 0xdb, 0xba, 0x1c, 0x83, 0xec, 0xba, 0xd3, 0x5f, 0x5c, 0x77, 0x5e, 0xa7, 0x46, 0x36, 0xec, 0x69, 0xa, 0x4b, 0x17, 0x79}}
|
||||
return a, nil
|
||||
}
|
||||
|
@ -180,7 +180,7 @@ func _1540715431_add_versionDownSql() (*asset, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "1540715431_add_version.down.sql", size: 127, mode: os.FileMode(0644), modTime: time.Unix(1564400388, 0)}
|
||||
info := bindataFileInfo{name: "1540715431_add_version.down.sql", size: 127, mode: os.FileMode(0644), modTime: time.Unix(1564484687, 0)}
|
||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xf5, 0x9, 0x4, 0xe3, 0x76, 0x2e, 0xb8, 0x9, 0x23, 0xf0, 0x70, 0x93, 0xc4, 0x50, 0xe, 0x9d, 0x84, 0x22, 0x8c, 0x94, 0xd3, 0x24, 0x9, 0x9a, 0xc1, 0xa1, 0x48, 0x45, 0xfd, 0x40, 0x6e, 0xe6}}
|
||||
return a, nil
|
||||
}
|
||||
|
@ -200,7 +200,7 @@ func _1540715431_add_versionUpSql() (*asset, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "1540715431_add_version.up.sql", size: 265, mode: os.FileMode(0644), modTime: time.Unix(1564400388, 0)}
|
||||
info := bindataFileInfo{name: "1540715431_add_version.up.sql", size: 265, mode: os.FileMode(0644), modTime: time.Unix(1564484687, 0)}
|
||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xc7, 0x4c, 0x36, 0x96, 0xdf, 0x16, 0x10, 0xa6, 0x27, 0x1a, 0x79, 0x8b, 0x42, 0x83, 0x23, 0xc, 0x7e, 0xb6, 0x3d, 0x2, 0xda, 0xa4, 0xb4, 0xd, 0x27, 0x55, 0xba, 0xdc, 0xb2, 0x88, 0x8f, 0xa6}}
|
||||
return a, nil
|
||||
}
|
||||
|
@ -220,7 +220,7 @@ func _1541164797_add_installationsDownSql() (*asset, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "1541164797_add_installations.down.sql", size: 26, mode: os.FileMode(0644), modTime: time.Unix(1564400388, 0)}
|
||||
info := bindataFileInfo{name: "1541164797_add_installations.down.sql", size: 26, mode: os.FileMode(0644), modTime: time.Unix(1564484687, 0)}
|
||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xf5, 0xfd, 0xe6, 0xd8, 0xca, 0x3b, 0x38, 0x18, 0xee, 0x0, 0x5f, 0x36, 0x9e, 0x1e, 0xd, 0x19, 0x3e, 0xb4, 0x73, 0x53, 0xe9, 0xa5, 0xac, 0xdd, 0xa1, 0x2f, 0xc7, 0x6c, 0xa8, 0xd9, 0xa, 0x88}}
|
||||
return a, nil
|
||||
}
|
||||
|
@ -240,7 +240,7 @@ func _1541164797_add_installationsUpSql() (*asset, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "1541164797_add_installations.up.sql", size: 216, mode: os.FileMode(0644), modTime: time.Unix(1564400388, 0)}
|
||||
info := bindataFileInfo{name: "1541164797_add_installations.up.sql", size: 216, mode: os.FileMode(0644), modTime: time.Unix(1564484687, 0)}
|
||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x2d, 0x18, 0x26, 0xb8, 0x88, 0x47, 0xdb, 0x83, 0xcc, 0xb6, 0x9d, 0x1c, 0x1, 0xae, 0x2f, 0xde, 0x97, 0x82, 0x3, 0x30, 0xa8, 0x63, 0xa1, 0x78, 0x4b, 0xa5, 0x9, 0x8, 0x75, 0xa2, 0x57, 0x81}}
|
||||
return a, nil
|
||||
}
|
||||
|
@ -260,7 +260,7 @@ func _1558084410_add_secretDownSql() (*asset, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "1558084410_add_secret.down.sql", size: 56, mode: os.FileMode(0644), modTime: time.Unix(1564400388, 0)}
|
||||
info := bindataFileInfo{name: "1558084410_add_secret.down.sql", size: 56, mode: os.FileMode(0644), modTime: time.Unix(1564484687, 0)}
|
||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x49, 0xb, 0x65, 0xdf, 0x59, 0xbf, 0xe9, 0x5, 0x5b, 0x6f, 0xd5, 0x3a, 0xb7, 0x57, 0xe8, 0x78, 0x38, 0x73, 0x53, 0x57, 0xf7, 0x24, 0x4, 0xe4, 0xa2, 0x49, 0x22, 0xa2, 0xc6, 0xfd, 0x80, 0xa4}}
|
||||
return a, nil
|
||||
}
|
||||
|
@ -280,7 +280,7 @@ func _1558084410_add_secretUpSql() (*asset, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "1558084410_add_secret.up.sql", size: 301, mode: os.FileMode(0644), modTime: time.Unix(1564400388, 0)}
|
||||
info := bindataFileInfo{name: "1558084410_add_secret.up.sql", size: 301, mode: os.FileMode(0644), modTime: time.Unix(1564484687, 0)}
|
||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xf5, 0x32, 0x36, 0x8e, 0x47, 0xb0, 0x8f, 0xc1, 0xc6, 0xf7, 0xc6, 0x9f, 0x2d, 0x44, 0x75, 0x2b, 0x26, 0xec, 0x6, 0xa0, 0x7b, 0xa5, 0xbd, 0xc8, 0x76, 0x8a, 0x82, 0x68, 0x2, 0x42, 0xb5, 0xf4}}
|
||||
return a, nil
|
||||
}
|
||||
|
@ -300,7 +300,7 @@ func _1558588866_add_versionDownSql() (*asset, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "1558588866_add_version.down.sql", size: 47, mode: os.FileMode(0644), modTime: time.Unix(1564400388, 0)}
|
||||
info := bindataFileInfo{name: "1558588866_add_version.down.sql", size: 47, mode: os.FileMode(0644), modTime: time.Unix(1564484687, 0)}
|
||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xde, 0x52, 0x34, 0x3c, 0x46, 0x4a, 0xf0, 0x72, 0x47, 0x6f, 0x49, 0x5c, 0xc7, 0xf9, 0x32, 0xce, 0xc4, 0x3d, 0xfd, 0x61, 0xa1, 0x8b, 0x8f, 0xf2, 0x31, 0x34, 0xde, 0x15, 0x49, 0xa6, 0xde, 0xb9}}
|
||||
return a, nil
|
||||
}
|
||||
|
@ -320,7 +320,7 @@ func _1558588866_add_versionUpSql() (*asset, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "1558588866_add_version.up.sql", size: 57, mode: os.FileMode(0644), modTime: time.Unix(1564400388, 0)}
|
||||
info := bindataFileInfo{name: "1558588866_add_version.up.sql", size: 57, mode: os.FileMode(0644), modTime: time.Unix(1564484687, 0)}
|
||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x2a, 0xea, 0x64, 0x39, 0x61, 0x20, 0x83, 0x83, 0xb, 0x2e, 0x79, 0x64, 0xb, 0x53, 0xfa, 0xfe, 0xc6, 0xf7, 0x67, 0x42, 0xd3, 0x4f, 0xdc, 0x7e, 0x30, 0x32, 0xe8, 0x14, 0x41, 0xe9, 0xe7, 0x3b}}
|
||||
return a, nil
|
||||
}
|
||||
|
@ -340,7 +340,7 @@ func _1559627659_add_contact_codeDownSql() (*asset, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "1559627659_add_contact_code.down.sql", size: 32, mode: os.FileMode(0644), modTime: time.Unix(1564400388, 0)}
|
||||
info := bindataFileInfo{name: "1559627659_add_contact_code.down.sql", size: 32, mode: os.FileMode(0644), modTime: time.Unix(1564484687, 0)}
|
||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x5d, 0x64, 0x6d, 0xce, 0x24, 0x42, 0x20, 0x8d, 0x4f, 0x37, 0xaa, 0x9d, 0xc, 0x57, 0x98, 0xc1, 0xd1, 0x1a, 0x34, 0xcd, 0x9f, 0x8f, 0x34, 0x86, 0xb3, 0xd3, 0xdc, 0xf1, 0x7d, 0xe5, 0x1b, 0x6e}}
|
||||
return a, nil
|
||||
}
|
||||
|
@ -360,7 +360,7 @@ func _1559627659_add_contact_codeUpSql() (*asset, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "1559627659_add_contact_code.up.sql", size: 198, mode: os.FileMode(0644), modTime: time.Unix(1564400388, 0)}
|
||||
info := bindataFileInfo{name: "1559627659_add_contact_code.up.sql", size: 198, mode: os.FileMode(0644), modTime: time.Unix(1564484687, 0)}
|
||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x16, 0xf6, 0xc2, 0x62, 0x9c, 0xd2, 0xc9, 0x1e, 0xd8, 0xea, 0xaa, 0xea, 0x95, 0x8f, 0x89, 0x6a, 0x85, 0x5d, 0x9d, 0x99, 0x78, 0x3c, 0x90, 0x66, 0x99, 0x3e, 0x4b, 0x19, 0x62, 0xfb, 0x31, 0x4d}}
|
||||
return a, nil
|
||||
}
|
||||
|
@ -380,7 +380,7 @@ func _1561368210_add_installation_metadataDownSql() (*asset, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "1561368210_add_installation_metadata.down.sql", size: 35, mode: os.FileMode(0644), modTime: time.Unix(1564400388, 0)}
|
||||
info := bindataFileInfo{name: "1561368210_add_installation_metadata.down.sql", size: 35, mode: os.FileMode(0644), modTime: time.Unix(1564484687, 0)}
|
||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xa8, 0xde, 0x3f, 0xd2, 0x4a, 0x50, 0x98, 0x56, 0xe3, 0xc0, 0xcd, 0x9d, 0xb0, 0x34, 0x3b, 0xe5, 0x62, 0x18, 0xb5, 0x20, 0xc9, 0x3e, 0xdc, 0x6a, 0x40, 0x36, 0x66, 0xea, 0x51, 0x8c, 0x71, 0xf5}}
|
||||
return a, nil
|
||||
}
|
||||
|
@ -400,7 +400,7 @@ func _1561368210_add_installation_metadataUpSql() (*asset, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "1561368210_add_installation_metadata.up.sql", size: 267, mode: os.FileMode(0644), modTime: time.Unix(1564400388, 0)}
|
||||
info := bindataFileInfo{name: "1561368210_add_installation_metadata.up.sql", size: 267, mode: os.FileMode(0644), modTime: time.Unix(1564484687, 0)}
|
||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xb4, 0x71, 0x8f, 0x29, 0xb1, 0xaa, 0xd6, 0xd1, 0x8c, 0x17, 0xef, 0x6c, 0xd5, 0x80, 0xb8, 0x2c, 0xc3, 0xfe, 0xec, 0x24, 0x4d, 0xc8, 0x25, 0xd3, 0xb4, 0xcd, 0xa9, 0xac, 0x63, 0x61, 0xb2, 0x9c}}
|
||||
return a, nil
|
||||
}
|
||||
|
@ -420,7 +420,7 @@ func docGo() (*asset, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "doc.go", size: 377, mode: os.FileMode(0644), modTime: time.Unix(1564400388, 0)}
|
||||
info := bindataFileInfo{name: "doc.go", size: 377, mode: os.FileMode(0644), modTime: time.Unix(1564484687, 0)}
|
||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xef, 0xaf, 0xdf, 0xcf, 0x65, 0xae, 0x19, 0xfc, 0x9d, 0x29, 0xc1, 0x91, 0xaf, 0xb5, 0xd5, 0xb1, 0x56, 0xf3, 0xee, 0xa8, 0xba, 0x13, 0x65, 0xdb, 0xab, 0xcf, 0x4e, 0xac, 0x92, 0xe9, 0x60, 0xf1}}
|
||||
return a, nil
|
||||
}
|
||||
|
|
64
messenger.go
64
messenger.go
|
@ -366,36 +366,44 @@ func (m *Messenger) Mailservers() ([]string, error) {
|
|||
}
|
||||
|
||||
func (m *Messenger) Join(chat Chat) error {
|
||||
if chat.PublicKey() != nil {
|
||||
return m.adapter.JoinPrivate(chat.PublicKey())
|
||||
} else if chat.PublicName() != "" {
|
||||
return m.adapter.JoinPublic(chat.PublicName())
|
||||
if chat.PublicKey != nil {
|
||||
return m.adapter.JoinPrivate(chat.PublicKey)
|
||||
} else if chat.Name != "" {
|
||||
return m.adapter.JoinPublic(chat.Name)
|
||||
}
|
||||
return errors.New("chat is neither public nor private")
|
||||
}
|
||||
|
||||
func (m *Messenger) Leave(chat Chat) error {
|
||||
if chat.PublicKey() != nil {
|
||||
return m.adapter.LeavePrivate(chat.PublicKey())
|
||||
} else if chat.PublicName() != "" {
|
||||
return m.adapter.LeavePublic(chat.PublicName())
|
||||
if chat.PublicKey != nil {
|
||||
return m.adapter.LeavePrivate(chat.PublicKey)
|
||||
} else if chat.Name != "" {
|
||||
return m.adapter.LeavePublic(chat.Name)
|
||||
}
|
||||
return errors.New("chat is neither public nor private")
|
||||
}
|
||||
|
||||
func (m *Messenger) SaveChat(chat Chat) error {
|
||||
return m.persistence.SaveChat(chat)
|
||||
}
|
||||
|
||||
func (m *Messenger) Chats(from, to int) ([]*Chat, error) {
|
||||
return m.persistence.Chats(from, to)
|
||||
}
|
||||
|
||||
func (m *Messenger) Send(ctx context.Context, chat Chat, data []byte) ([]byte, error) {
|
||||
chatID := chat.ID()
|
||||
chatID := chat.ID
|
||||
if chatID == "" {
|
||||
return nil, ErrChatIDEmpty
|
||||
}
|
||||
|
||||
clock, err := m.persistence.LastMessageClock(chat.ID())
|
||||
clock, err := m.persistence.LastMessageClock(chat.ID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if chat.PublicKey() != nil {
|
||||
hash, message, err := m.adapter.SendPrivate(ctx, chat.PublicKey(), chat.ID(), data, clock)
|
||||
if chat.PublicKey != nil {
|
||||
hash, message, err := m.adapter.SendPrivate(ctx, chat.PublicKey, chat.ID, data, clock)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -405,7 +413,7 @@ func (m *Messenger) Send(ctx context.Context, chat Chat, data []byte) ([]byte, e
|
|||
message.SigPubKey = &m.identity.PublicKey
|
||||
|
||||
if m.messagesPersistenceEnabled {
|
||||
_, err = m.persistence.SaveMessages(chat.ID(), []*protocol.Message{message})
|
||||
_, err = m.persistence.SaveMessages(chat.ID, []*protocol.Message{message})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -415,8 +423,8 @@ func (m *Messenger) Send(ctx context.Context, chat Chat, data []byte) ([]byte, e
|
|||
m.ownMessages[chatID] = append(m.ownMessages[chatID], message)
|
||||
|
||||
return hash, nil
|
||||
} else if chat.PublicName() != "" {
|
||||
return m.adapter.SendPublic(ctx, chat.PublicName(), chat.ID(), data, clock)
|
||||
} else if chat.Name != "" {
|
||||
return m.adapter.SendPublic(ctx, chat.Name, chat.ID, data, clock)
|
||||
}
|
||||
return nil, errors.New("chat is neither public nor private")
|
||||
}
|
||||
|
@ -424,10 +432,10 @@ func (m *Messenger) Send(ctx context.Context, chat Chat, data []byte) ([]byte, e
|
|||
// SendRaw takes encoded data, encrypts it and sends through the wire.
|
||||
// DEPRECATED
|
||||
func (m *Messenger) SendRaw(ctx context.Context, chat Chat, data []byte) ([]byte, whisper.NewMessage, error) {
|
||||
if chat.PublicKey() != nil {
|
||||
return m.adapter.SendPrivateRaw(ctx, chat.PublicKey(), data)
|
||||
} else if chat.PublicName() != "" {
|
||||
return m.adapter.SendPublicRaw(ctx, chat.PublicName(), data)
|
||||
if chat.PublicKey != nil {
|
||||
return m.adapter.SendPrivateRaw(ctx, chat.PublicKey, data)
|
||||
} else if chat.Name != "" {
|
||||
return m.adapter.SendPublicRaw(ctx, chat.Name, data)
|
||||
}
|
||||
return nil, whisper.NewMessage{}, errors.New("chat is neither public nor private")
|
||||
}
|
||||
|
@ -491,14 +499,14 @@ func (m *Messenger) Retrieve(ctx context.Context, chat Chat, c RetrieveConfig) (
|
|||
ownLatest []*protocol.Message
|
||||
)
|
||||
|
||||
if chat.PublicKey() != nil {
|
||||
latest, err = m.adapter.RetrievePrivateMessages(chat.PublicKey())
|
||||
if chat.PublicKey != nil {
|
||||
latest, err = m.adapter.RetrievePrivateMessages(chat.PublicKey)
|
||||
// Return any own messages for this chat as well.
|
||||
if ownMessages, ok := m.ownMessages[chat.ID()]; ok {
|
||||
if ownMessages, ok := m.ownMessages[chat.ID]; ok {
|
||||
ownLatest = ownMessages
|
||||
}
|
||||
} else if chat.PublicName() != "" {
|
||||
latest, err = m.adapter.RetrievePublicMessages(chat.PublicName())
|
||||
} else if chat.Name != "" {
|
||||
latest, err = m.adapter.RetrievePublicMessages(chat.Name)
|
||||
} else {
|
||||
return nil, errors.New("chat is neither public nor private")
|
||||
}
|
||||
|
@ -509,14 +517,14 @@ func (m *Messenger) Retrieve(ctx context.Context, chat Chat, c RetrieveConfig) (
|
|||
}
|
||||
|
||||
if m.messagesPersistenceEnabled {
|
||||
_, err = m.persistence.SaveMessages(chat.ID(), latest)
|
||||
_, err = m.persistence.SaveMessages(chat.ID, latest)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to save latest messages")
|
||||
}
|
||||
}
|
||||
|
||||
// Confirm received and decrypted messages.
|
||||
if m.messagesPersistenceEnabled && chat.PublicKey() != nil {
|
||||
if m.messagesPersistenceEnabled && chat.PublicKey != nil {
|
||||
for _, message := range latest {
|
||||
// Confirm received and decrypted messages.
|
||||
if err := m.encryptor.ConfirmMessageProcessed(message.ID); err != nil {
|
||||
|
@ -526,13 +534,13 @@ func (m *Messenger) Retrieve(ctx context.Context, chat Chat, c RetrieveConfig) (
|
|||
}
|
||||
|
||||
// We may need to add more messages from the past.
|
||||
result, err := m.retrieveSaved(ctx, chat.ID(), c, append(latest, ownLatest...))
|
||||
result, err := m.retrieveSaved(ctx, chat.ID, c, append(latest, ownLatest...))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// When our messages are returned, we can delete them.
|
||||
delete(m.ownMessages, chat.ID())
|
||||
delete(m.ownMessages, chat.ID)
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ import (
|
|||
"context"
|
||||
"crypto/ecdsa"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
|
@ -18,27 +19,6 @@ import (
|
|||
whisper "github.com/status-im/whisper/whisperv6"
|
||||
)
|
||||
|
||||
type testChat struct {
|
||||
publicName string
|
||||
publicKey *ecdsa.PublicKey
|
||||
}
|
||||
|
||||
func (c testChat) ID() string {
|
||||
if c.publicKey != nil {
|
||||
return hex.EncodeToString(crypto.FromECDSAPub(c.publicKey))
|
||||
}
|
||||
// Deliberately use a different ID than public name.
|
||||
return hex.EncodeToString([]byte(c.publicName))
|
||||
}
|
||||
|
||||
func (c testChat) PublicKey() *ecdsa.PublicKey {
|
||||
return c.publicKey
|
||||
}
|
||||
|
||||
func (c testChat) PublicName() string {
|
||||
return c.publicName
|
||||
}
|
||||
|
||||
func TestMessengerSuite(t *testing.T) {
|
||||
suite.Run(t, new(MessengerSuite))
|
||||
}
|
||||
|
@ -87,19 +67,19 @@ func (s *MessengerSuite) TearDownTest() {
|
|||
}
|
||||
|
||||
func (s *MessengerSuite) TestSendPublic() {
|
||||
_, err := s.m.Send(context.Background(), testChat{publicName: "status"}, []byte("test"))
|
||||
_, err := s.m.Send(context.Background(), Chat{Name: "status", ID: "status"}, []byte("test"))
|
||||
s.NoError(err)
|
||||
}
|
||||
|
||||
func (s *MessengerSuite) TestSendPrivate() {
|
||||
recipientKey, err := crypto.GenerateKey()
|
||||
s.NoError(err)
|
||||
_, err = s.m.Send(context.Background(), testChat{publicKey: &recipientKey.PublicKey}, []byte("test"))
|
||||
_, err = s.m.Send(context.Background(), Chat{ID: "x", PublicKey: &recipientKey.PublicKey}, []byte("test"))
|
||||
s.NoError(err)
|
||||
}
|
||||
|
||||
func (s *MessengerSuite) TestRetrievePublic() {
|
||||
chat := testChat{publicName: "status"}
|
||||
chat := Chat{ID: "status", Name: "status"}
|
||||
|
||||
_, err := s.m.Send(context.Background(), chat, []byte("test"))
|
||||
s.NoError(err)
|
||||
|
@ -126,7 +106,7 @@ func (s *MessengerSuite) TestRetrievePublic() {
|
|||
func (s *MessengerSuite) TestRetrievePrivate() {
|
||||
publicContact, err := crypto.GenerateKey()
|
||||
s.NoError(err)
|
||||
chat := testChat{publicKey: &publicContact.PublicKey}
|
||||
chat := Chat{ID: "x", PublicKey: &publicContact.PublicKey}
|
||||
|
||||
_, err = s.m.Send(context.Background(), chat, []byte("test"))
|
||||
s.NoError(err)
|
||||
|
@ -150,6 +130,197 @@ func (s *MessengerSuite) TestRetrievePrivate() {
|
|||
s.Equal(&s.privateKey.PublicKey, message.SigPubKey) // this is OUR message
|
||||
}
|
||||
|
||||
func (s *MessengerSuite) TestChatPersistencePublic() {
|
||||
chat := Chat{
|
||||
ID: "chat-name",
|
||||
Name: "chat-name",
|
||||
Color: "#fffff",
|
||||
Active: true,
|
||||
ChatType: ChatTypePublic,
|
||||
Timestamp: 10,
|
||||
LastClockValue: 20,
|
||||
DeletedAtClockValue: 30,
|
||||
UnviewedMessagesCount: 40,
|
||||
LastMessageContentType: "something",
|
||||
LastMessageContent: "something-else",
|
||||
}
|
||||
|
||||
s.Require().NoError(s.m.SaveChat(chat))
|
||||
savedChats, err := s.m.Chats(0, 10)
|
||||
s.Require().NoError(err)
|
||||
s.Require().Equal(1, len(savedChats))
|
||||
|
||||
actualChat := savedChats[0]
|
||||
expectedChat := &chat
|
||||
|
||||
s.Require().Equal(actualChat, expectedChat)
|
||||
}
|
||||
|
||||
func (s *MessengerSuite) TestChatPersistenceUpdate() {
|
||||
chat := Chat{
|
||||
ID: "chat-name",
|
||||
Name: "chat-name",
|
||||
Color: "#fffff",
|
||||
Active: true,
|
||||
ChatType: ChatTypePublic,
|
||||
Timestamp: 10,
|
||||
LastClockValue: 20,
|
||||
DeletedAtClockValue: 30,
|
||||
UnviewedMessagesCount: 40,
|
||||
LastMessageContentType: "something",
|
||||
LastMessageContent: "something-else",
|
||||
}
|
||||
|
||||
s.Require().NoError(s.m.SaveChat(chat))
|
||||
savedChats, err := s.m.Chats(0, 10)
|
||||
s.Require().NoError(err)
|
||||
s.Require().Equal(1, len(savedChats))
|
||||
|
||||
actualChat := savedChats[0]
|
||||
expectedChat := &chat
|
||||
|
||||
s.Require().Equal(expectedChat, actualChat)
|
||||
|
||||
chat.Name = "updated-name"
|
||||
s.Require().NoError(s.m.SaveChat(chat))
|
||||
updatedChats, err := s.m.Chats(0, 10)
|
||||
s.Require().NoError(err)
|
||||
s.Require().Equal(1, len(updatedChats))
|
||||
|
||||
actualUpdatedChat := updatedChats[0]
|
||||
expectedUpdatedChat := &chat
|
||||
|
||||
s.Require().Equal(expectedUpdatedChat, actualUpdatedChat)
|
||||
}
|
||||
|
||||
func (s *MessengerSuite) TestChatPagination() {
|
||||
for i := 0; i <= 20; i++ {
|
||||
chat := Chat{
|
||||
ID: fmt.Sprintf("chat-name-%d", i),
|
||||
Name: "chat-name",
|
||||
Color: "#fffff",
|
||||
Active: true,
|
||||
ChatType: ChatTypePublic,
|
||||
Timestamp: int64(i),
|
||||
LastClockValue: 20,
|
||||
DeletedAtClockValue: 30,
|
||||
UnviewedMessagesCount: 40,
|
||||
LastMessageContentType: "something",
|
||||
LastMessageContent: "something-else",
|
||||
}
|
||||
|
||||
s.Require().NoError(s.m.SaveChat(chat))
|
||||
}
|
||||
firstPageChats, err := s.m.Chats(0, 10)
|
||||
s.Require().NoError(err)
|
||||
s.Require().Equal(10, len(firstPageChats))
|
||||
s.Require().Equal(int64(20), firstPageChats[0].Timestamp)
|
||||
s.Require().Equal(int64(11), firstPageChats[9].Timestamp)
|
||||
|
||||
secondPageChats, err := s.m.Chats(10, -1)
|
||||
s.Require().NoError(err)
|
||||
s.Require().Equal(11, len(secondPageChats))
|
||||
s.Require().Equal(int64(10), secondPageChats[0].Timestamp)
|
||||
s.Require().Equal(int64(0), secondPageChats[10].Timestamp)
|
||||
}
|
||||
|
||||
func (s *MessengerSuite) TestChatPersistenceOneToOne() {
|
||||
pkStr := "0x0424a68f89ba5fcd5e0640c1e1f591d561fa4125ca4e2a43592bc4123eca10ce064e522c254bb83079ba404327f6eafc01ec90a1444331fe769d3f3a7f90b0dde1"
|
||||
chat := Chat{
|
||||
ID: pkStr,
|
||||
Name: pkStr,
|
||||
Color: "#fffff",
|
||||
Active: true,
|
||||
ChatType: ChatTypeOneToOne,
|
||||
Timestamp: 10,
|
||||
LastClockValue: 20,
|
||||
DeletedAtClockValue: 30,
|
||||
UnviewedMessagesCount: 40,
|
||||
LastMessageContentType: "something",
|
||||
LastMessageContent: "something-else",
|
||||
}
|
||||
publicKeyBytes, err := hex.DecodeString(pkStr[2:])
|
||||
s.Require().NoError(err)
|
||||
|
||||
pk, err := crypto.UnmarshalPubkey(publicKeyBytes)
|
||||
s.Require().NoError(err)
|
||||
|
||||
s.Require().NoError(s.m.SaveChat(chat))
|
||||
savedChats, err := s.m.Chats(0, 10)
|
||||
s.Require().NoError(err)
|
||||
s.Require().Equal(1, len(savedChats))
|
||||
|
||||
actualChat := savedChats[0]
|
||||
expectedChat := &chat
|
||||
expectedChat.PublicKey = pk
|
||||
|
||||
s.Require().Equal(expectedChat, actualChat)
|
||||
}
|
||||
|
||||
func (s *MessengerSuite) TestChatPersistencePrivateGroupChat() {
|
||||
chat := Chat{
|
||||
ID: "chat-id",
|
||||
Name: "chat-id",
|
||||
Color: "#fffff",
|
||||
Active: true,
|
||||
ChatType: ChatTypePrivateGroupChat,
|
||||
Timestamp: 10,
|
||||
Members: []ChatMember{
|
||||
ChatMember{
|
||||
ID: "1",
|
||||
Admin: false,
|
||||
Joined: true,
|
||||
},
|
||||
ChatMember{
|
||||
ID: "2",
|
||||
Admin: true,
|
||||
Joined: false,
|
||||
},
|
||||
ChatMember{
|
||||
ID: "3",
|
||||
Admin: true,
|
||||
Joined: true,
|
||||
},
|
||||
},
|
||||
MembershipUpdates: []ChatMembershipUpdate{
|
||||
ChatMembershipUpdate{
|
||||
ID: "1",
|
||||
Type: "type-1",
|
||||
Name: "name-1",
|
||||
ClockValue: 1,
|
||||
Signature: "signature-1",
|
||||
From: "from-1",
|
||||
Member: "member-1",
|
||||
Members: []string{"member-1", "member-2"},
|
||||
},
|
||||
ChatMembershipUpdate{
|
||||
ID: "2",
|
||||
Type: "type-2",
|
||||
Name: "name-2",
|
||||
ClockValue: 2,
|
||||
Signature: "signature-2",
|
||||
From: "from-2",
|
||||
Member: "member-2",
|
||||
Members: []string{"member-2", "member-3"},
|
||||
},
|
||||
},
|
||||
LastClockValue: 20,
|
||||
DeletedAtClockValue: 30,
|
||||
UnviewedMessagesCount: 40,
|
||||
LastMessageContentType: "something",
|
||||
LastMessageContent: "something-else",
|
||||
}
|
||||
s.Require().NoError(s.m.SaveChat(chat))
|
||||
savedChats, err := s.m.Chats(0, 10)
|
||||
s.Require().NoError(err)
|
||||
s.Require().Equal(1, len(savedChats))
|
||||
|
||||
actualChat := savedChats[0]
|
||||
expectedChat := &chat
|
||||
|
||||
s.Require().Equal(expectedChat, actualChat)
|
||||
}
|
||||
|
||||
func (s *MessengerSuite) TestSharedSecretHandler() {
|
||||
err := s.m.handleSharedSecrets(nil)
|
||||
s.NoError(err)
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
// sources:
|
||||
// 000001_init.down.db.sql (82B)
|
||||
// 000001_init.up.db.sql (840B)
|
||||
// 000002_add_chats.down.db.sql (74B)
|
||||
// 000002_add_chats.up.db.sql (541B)
|
||||
// doc.go (377B)
|
||||
|
||||
package migrations
|
||||
|
@ -86,7 +88,7 @@ func _000001_initDownDbSql() (*asset, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "000001_init.down.db.sql", size: 82, mode: os.FileMode(0644), modTime: time.Unix(1564400388, 0)}
|
||||
info := bindataFileInfo{name: "000001_init.down.db.sql", size: 82, mode: os.FileMode(0644), modTime: time.Unix(1564484687, 0)}
|
||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xe8, 0x5f, 0xe0, 0x6, 0xfc, 0xed, 0xb7, 0xff, 0xb5, 0xf3, 0x33, 0x45, 0x1, 0x5b, 0x84, 0x80, 0x74, 0x60, 0x81, 0xa6, 0x8b, 0xb4, 0xd4, 0xad, 0x10, 0xa8, 0xb3, 0x61, 0x6f, 0xc5, 0x2f, 0xaa}}
|
||||
return a, nil
|
||||
}
|
||||
|
@ -106,11 +108,51 @@ func _000001_initUpDbSql() (*asset, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "000001_init.up.db.sql", size: 840, mode: os.FileMode(0644), modTime: time.Unix(1564400388, 0)}
|
||||
info := bindataFileInfo{name: "000001_init.up.db.sql", size: 840, mode: os.FileMode(0644), modTime: time.Unix(1564484687, 0)}
|
||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xe7, 0x27, 0x96, 0x3b, 0x72, 0x81, 0x7d, 0xba, 0xa4, 0xfb, 0xf7, 0x4, 0xd, 0x6f, 0xc8, 0x30, 0xfe, 0x47, 0xe0, 0x9, 0xf, 0x43, 0x13, 0x6, 0x55, 0xfc, 0xee, 0x15, 0x69, 0x99, 0x53, 0x3f}}
|
||||
return a, nil
|
||||
}
|
||||
|
||||
var __000002_add_chatsDownDbSql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x72\x09\xf2\x0f\x50\x08\x71\x74\xf2\x71\x55\xc8\x4d\xcd\x4d\x4a\x2d\x2a\xce\xc8\x2c\x88\x2f\x2d\x48\x49\x2c\x49\x2d\xb6\xe6\x42\x92\x4e\xce\x48\x2c\x89\x87\xaa\xc1\x90\x28\xb6\xe6\x02\x04\x00\x00\xff\xff\xde\x59\xf6\x29\x4a\x00\x00\x00")
|
||||
|
||||
func _000002_add_chatsDownDbSqlBytes() ([]byte, error) {
|
||||
return bindataRead(
|
||||
__000002_add_chatsDownDbSql,
|
||||
"000002_add_chats.down.db.sql",
|
||||
)
|
||||
}
|
||||
|
||||
func _000002_add_chatsDownDbSql() (*asset, error) {
|
||||
bytes, err := _000002_add_chatsDownDbSqlBytes()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "000002_add_chats.down.db.sql", size: 74, mode: os.FileMode(0644), modTime: time.Unix(1564502835, 0)}
|
||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xd3, 0xa7, 0xf0, 0x94, 0x7a, 0x9, 0xdc, 0x6c, 0x7b, 0xdc, 0x12, 0x30, 0x55, 0x31, 0x17, 0xf2, 0xcc, 0x6e, 0xfd, 0xbb, 0x70, 0xb9, 0xd8, 0x9f, 0x81, 0x83, 0xdc, 0x1d, 0x1c, 0x3a, 0x8d, 0xce}}
|
||||
return a, nil
|
||||
}
|
||||
|
||||
var __000002_add_chatsUpDbSql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x8c\x91\x4f\x4b\x03\x31\x10\xc5\xef\xf3\x29\x06\x3c\x54\x61\x0f\x7a\x10\x85\x9e\xb2\xdb\x14\x17\xe3\xa6\xa4\xa9\xd8\x53\x48\xb3\x83\x5d\xba\xff\x68\xb2\x95\x7e\x7b\x69\x5d\xda\xea\x2a\x78\x4c\xde\xef\xbd\x49\xe6\x25\x8a\x33\xcd\x51\xb3\x58\x70\x4c\xa7\x98\x49\x8d\xfc\x2d\x9d\xeb\x39\xba\xb5\x0d\x1e\xaf\xa1\xc8\xf1\x95\xa9\xe4\x89\x29\x9c\xa9\xf4\x85\xa9\x25\x3e\xf3\x25\xca\x0c\x13\x99\x4d\x45\x9a\x68\x54\x7c\x26\x58\xc2\x23\xa8\x6d\x45\x27\xfa\x90\x95\x2d\x84\x88\xc0\x35\x65\xb3\x1d\xdc\xe3\x84\x4f\xd9\x42\x68\x1c\x5d\xd9\xbb\xc7\x87\xfc\x7e\x14\x41\xd8\xb7\x84\x69\xa6\x2f\xcc\xd6\x85\x62\x47\x18\x4b\x29\x38\xcb\x86\x6e\xad\x16\x3c\x82\x50\x54\xe4\x83\xad\xda\x1f\xee\x9c\x4a\x0a\x94\x1b\x1b\x8c\x2b\x1b\xb7\x31\x3b\x5b\x76\xdf\x47\x9c\x92\x6e\x23\x68\xbb\x55\x59\x38\xb3\xa1\x3d\xc6\x42\xc6\x11\x74\xf5\xae\xa0\x0f\xca\x4d\x45\xde\xdb\x77\x32\xae\xe9\xea\xf0\xa7\xbf\xb4\xfe\x7f\x83\x8e\xe0\x39\xb3\x0e\x54\x07\x73\xfc\x7d\xbf\xa6\xdf\x91\xb3\x5a\x51\xb5\xa2\xad\xef\x9f\xd9\x9f\xd6\x45\x6b\xba\x36\xb7\x81\xbe\x04\xb8\x19\x03\xc0\x44\xc9\x59\x5f\xf1\x90\x1b\x5f\xca\x87\xce\x4d\xcf\x8c\xe1\x33\x00\x00\xff\xff\xf3\xb4\xa4\xad\x1d\x02\x00\x00")
|
||||
|
||||
func _000002_add_chatsUpDbSqlBytes() ([]byte, error) {
|
||||
return bindataRead(
|
||||
__000002_add_chatsUpDbSql,
|
||||
"000002_add_chats.up.db.sql",
|
||||
)
|
||||
}
|
||||
|
||||
func _000002_add_chatsUpDbSql() (*asset, error) {
|
||||
bytes, err := _000002_add_chatsUpDbSqlBytes()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "000002_add_chats.up.db.sql", size: 541, mode: os.FileMode(0644), modTime: time.Unix(1564587096, 0)}
|
||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xd, 0x7f, 0x3a, 0xd7, 0xf6, 0x8b, 0x6e, 0x4d, 0xce, 0x7d, 0x63, 0x1d, 0x30, 0xa2, 0xc1, 0xb, 0xa0, 0x35, 0x2e, 0xfa, 0xef, 0xf0, 0x39, 0xf7, 0x22, 0xdd, 0x31, 0x11, 0xb1, 0xff, 0xbf, 0xb3}}
|
||||
return a, nil
|
||||
}
|
||||
|
||||
var _docGo = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x84\x8f\xbb\x6e\xc3\x30\x0c\x45\x77\x7f\xc5\x45\x96\x2c\xb5\xb4\x74\xea\xd6\xb1\x7b\x7f\x80\x91\x68\x89\x88\x1e\xae\x48\xe7\xf1\xf7\x85\xd3\x02\xcd\xd6\xf5\x00\xe7\xf0\xd2\x7b\x7c\x66\x51\x2c\x52\x18\xa2\x68\x1c\x58\x95\xc6\x1d\x27\x0e\xb4\x29\xe3\x90\xc4\xf2\x76\x72\xa1\x57\xaf\x46\xb6\xe9\x2c\xd5\x57\x49\x83\x8c\xfd\xe5\xf5\x30\x79\x8f\x40\xed\x68\xc8\xd4\x62\xe1\x47\x4b\xa1\x46\xc3\xa4\x25\x5c\xc5\x32\x08\xeb\xe0\x45\x6e\x0e\xef\x86\xc2\xa4\x06\xcb\x64\x47\x85\x65\x46\x20\xe5\x3d\xb3\xf4\x81\xd4\xe7\x93\xb4\x48\x46\x6e\x47\x1f\xcb\x13\xd9\x17\x06\x2a\x85\x23\x96\xd1\xeb\xc3\x55\xaa\x8c\x28\x83\x83\xf5\x71\x7f\x01\xa9\xb2\xa1\x51\x65\xdd\xfd\x4c\x17\x46\xeb\xbf\xe7\x41\x2d\xfe\xff\x11\xae\x7d\x9c\x15\xa4\xe0\xdb\xca\xc1\x38\xba\x69\x5a\x29\x9c\x29\x31\xf4\xab\x88\xf1\x34\x79\x9f\xfa\x5b\xe2\xc6\xbb\xf5\xbc\x71\x5e\xcf\x09\x3f\x35\xe9\x4d\x31\x77\x38\xe7\xff\x80\x4b\x1d\x6e\xfa\x0e\x00\x00\xff\xff\x9d\x60\x3d\x88\x79\x01\x00\x00")
|
||||
|
||||
func docGoBytes() ([]byte, error) {
|
||||
|
@ -126,7 +168,7 @@ func docGo() (*asset, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "doc.go", size: 377, mode: os.FileMode(0644), modTime: time.Unix(1564400388, 0)}
|
||||
info := bindataFileInfo{name: "doc.go", size: 377, mode: os.FileMode(0644), modTime: time.Unix(1564484687, 0)}
|
||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xef, 0xaf, 0xdf, 0xcf, 0x65, 0xae, 0x19, 0xfc, 0x9d, 0x29, 0xc1, 0x91, 0xaf, 0xb5, 0xd5, 0xb1, 0x56, 0xf3, 0xee, 0xa8, 0xba, 0x13, 0x65, 0xdb, 0xab, 0xcf, 0x4e, 0xac, 0x92, 0xe9, 0x60, 0xf1}}
|
||||
return a, nil
|
||||
}
|
||||
|
@ -226,6 +268,10 @@ var _bindata = map[string]func() (*asset, error){
|
|||
|
||||
"000001_init.up.db.sql": _000001_initUpDbSql,
|
||||
|
||||
"000002_add_chats.down.db.sql": _000002_add_chatsDownDbSql,
|
||||
|
||||
"000002_add_chats.up.db.sql": _000002_add_chatsUpDbSql,
|
||||
|
||||
"doc.go": docGo,
|
||||
}
|
||||
|
||||
|
@ -270,9 +316,11 @@ type bintree struct {
|
|||
}
|
||||
|
||||
var _bintree = &bintree{nil, map[string]*bintree{
|
||||
"000001_init.down.db.sql": &bintree{_000001_initDownDbSql, map[string]*bintree{}},
|
||||
"000001_init.up.db.sql": &bintree{_000001_initUpDbSql, map[string]*bintree{}},
|
||||
"doc.go": &bintree{docGo, map[string]*bintree{}},
|
||||
"000001_init.down.db.sql": &bintree{_000001_initDownDbSql, map[string]*bintree{}},
|
||||
"000001_init.up.db.sql": &bintree{_000001_initUpDbSql, map[string]*bintree{}},
|
||||
"000002_add_chats.down.db.sql": &bintree{_000002_add_chatsDownDbSql, map[string]*bintree{}},
|
||||
"000002_add_chats.up.db.sql": &bintree{_000002_add_chatsUpDbSql, map[string]*bintree{}},
|
||||
"doc.go": &bintree{docGo, map[string]*bintree{}},
|
||||
}}
|
||||
|
||||
// RestoreAsset restores an asset under the given directory.
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
DROP TABLE membership_updates;
|
||||
DROP TABLE chat_members;
|
||||
DROP TABLE chats;
|
|
@ -0,0 +1,20 @@
|
|||
CREATE TABLE IF NOT EXISTS chats (
|
||||
id VARCHAR PRIMARY KEY ON CONFLICT REPLACE,
|
||||
name VARCHAR NOT NULL,
|
||||
color VARCHAR NOT NULL DEFAULT '#a187d5',
|
||||
type INT NOT NULL,
|
||||
active BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
timestamp INT NOT NULL,
|
||||
deleted_at_clock_value INT NOT NULL DEFAULT 0,
|
||||
public_key BLOB,
|
||||
unviewed_message_count INT NOT NULL DEFAULT 0,
|
||||
last_clock_value INT NOT NULL DEFAULT 0,
|
||||
last_message_content_type VARCHAR,
|
||||
last_message_content VARCHAR,
|
||||
members BLOB,
|
||||
membership_updates BLOB
|
||||
);
|
||||
|
||||
|
||||
DROP TABLE membership_updates;
|
||||
DROP TABLE chat_members;
|
155
persistence.go
155
persistence.go
|
@ -1,11 +1,16 @@
|
|||
package statusproto
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/ecdsa"
|
||||
"database/sql"
|
||||
"encoding/gob"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/crypto/secp256k1"
|
||||
"github.com/pkg/errors"
|
||||
|
||||
|
@ -92,6 +97,151 @@ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`)
|
|||
return
|
||||
}
|
||||
|
||||
func (db sqlitePersistence) SaveChat(chat Chat) error {
|
||||
var err error
|
||||
// We build the db chatID using the type, so that we have no clashes
|
||||
chatID := fmt.Sprintf("%s-%d", chat.ID, chat.ChatType)
|
||||
|
||||
pkey := []byte{}
|
||||
// For one to one chatID is an encoded public key
|
||||
if chat.ChatType == ChatTypeOneToOne {
|
||||
pkey, err = hex.DecodeString(chat.ID[2:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Safety check, make sure is well formed
|
||||
_, err := crypto.UnmarshalPubkey(pkey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Encode members
|
||||
var encodedMembers bytes.Buffer
|
||||
memberEncoder := gob.NewEncoder(&encodedMembers)
|
||||
|
||||
if err := memberEncoder.Encode(chat.Members); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Encode membership updates
|
||||
var encodedMembershipUpdates bytes.Buffer
|
||||
membershipUpdatesEncoder := gob.NewEncoder(&encodedMembershipUpdates)
|
||||
|
||||
if err := membershipUpdatesEncoder.Encode(chat.MembershipUpdates); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Insert record
|
||||
stmt, err := db.db.Prepare(`INSERT INTO chats(id, name, color, active, type, timestamp, deleted_at_clock_value, public_key, unviewed_message_count, last_clock_value, last_message_content_type, last_message_content, members, membership_updates)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer stmt.Close()
|
||||
|
||||
_, err = stmt.Exec(
|
||||
chatID,
|
||||
chat.Name,
|
||||
chat.Color,
|
||||
chat.Active,
|
||||
chat.ChatType,
|
||||
chat.Timestamp,
|
||||
chat.DeletedAtClockValue,
|
||||
pkey,
|
||||
chat.UnviewedMessagesCount,
|
||||
chat.LastClockValue,
|
||||
chat.LastMessageContentType,
|
||||
chat.LastMessageContent,
|
||||
encodedMembers.Bytes(),
|
||||
encodedMembershipUpdates.Bytes(),
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (db sqlitePersistence) Chats(from, to int) ([]*Chat, error) {
|
||||
|
||||
rows, err := db.db.Query(`SELECT
|
||||
id,
|
||||
name,
|
||||
color,
|
||||
active,
|
||||
type,
|
||||
timestamp,
|
||||
deleted_at_clock_value,
|
||||
public_key,
|
||||
unviewed_message_count,
|
||||
last_clock_value,
|
||||
last_message_content_type,
|
||||
last_message_content,
|
||||
members,
|
||||
membership_updates
|
||||
FROM chats
|
||||
ORDER BY chats.timestamp DESC LIMIT ? OFFSET ?`, to, from)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var response []*Chat
|
||||
|
||||
for rows.Next() {
|
||||
chat := &Chat{}
|
||||
encodedMembers := []byte{}
|
||||
encodedMembershipUpdates := []byte{}
|
||||
pkey := []byte{}
|
||||
err := rows.Scan(
|
||||
&chat.ID,
|
||||
&chat.Name,
|
||||
&chat.Color,
|
||||
&chat.Active,
|
||||
&chat.ChatType,
|
||||
&chat.Timestamp,
|
||||
&chat.DeletedAtClockValue,
|
||||
&pkey,
|
||||
&chat.UnviewedMessagesCount,
|
||||
&chat.LastClockValue,
|
||||
&chat.LastMessageContentType,
|
||||
&chat.LastMessageContent,
|
||||
&encodedMembers,
|
||||
&encodedMembershipUpdates,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Restore the backward compatible ID
|
||||
chat.ID = chat.ID[:len(chat.ID)-2]
|
||||
|
||||
// Restore members
|
||||
membersDecoder := gob.NewDecoder(bytes.NewBuffer(encodedMembers))
|
||||
if err := membersDecoder.Decode(&chat.Members); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Restore membership updates
|
||||
membershipUpdatesDecoder := gob.NewDecoder(bytes.NewBuffer(encodedMembershipUpdates))
|
||||
if err := membershipUpdatesDecoder.Decode(&chat.MembershipUpdates); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(pkey) != 0 {
|
||||
chat.PublicKey, err = crypto.UnmarshalPubkey(pkey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
response = append(response, chat)
|
||||
}
|
||||
|
||||
return response, nil
|
||||
}
|
||||
|
||||
// Messages returns messages for a given contact, in a given period. Ordered by a timestamp.
|
||||
func (db sqlitePersistence) Messages(chatID string, from, to time.Time) (result []*protocol.Message, err error) {
|
||||
rows, err := db.db.Query(`SELECT
|
||||
|
@ -101,6 +251,8 @@ FROM user_messages WHERE chat_id = ? AND timestamp >= ? AND timestamp <= ? ORDER
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var (
|
||||
rst = []*protocol.Message{}
|
||||
)
|
||||
|
@ -134,6 +286,8 @@ FROM user_messages WHERE chat_id = ? AND rowid >= ? ORDER BY clock`,
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var (
|
||||
rst = []*protocol.Message{}
|
||||
)
|
||||
|
@ -185,6 +339,7 @@ func (db sqlitePersistence) UnreadMessages(chatID string) ([]*protocol.Message,
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var result []*protocol.Message
|
||||
|
||||
|
|
|
@ -86,7 +86,7 @@ func _1561059285_add_whisper_keysDownSql() (*asset, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "1561059285_add_whisper_keys.down.sql", size: 25, mode: os.FileMode(0644), modTime: time.Unix(1564400388, 0)}
|
||||
info := bindataFileInfo{name: "1561059285_add_whisper_keys.down.sql", size: 25, mode: os.FileMode(0644), modTime: time.Unix(1564484687, 0)}
|
||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xb9, 0x31, 0x3f, 0xce, 0xfa, 0x44, 0x36, 0x1b, 0xb0, 0xec, 0x5d, 0xb, 0x90, 0xb, 0x21, 0x4f, 0xd5, 0xe5, 0x50, 0xed, 0xc7, 0x43, 0xdf, 0x83, 0xb4, 0x3a, 0xc1, 0x55, 0x2e, 0x53, 0x7c, 0x67}}
|
||||
return a, nil
|
||||
}
|
||||
|
@ -106,7 +106,7 @@ func _1561059285_add_whisper_keysUpSql() (*asset, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "1561059285_add_whisper_keys.up.sql", size: 112, mode: os.FileMode(0644), modTime: time.Unix(1564400388, 0)}
|
||||
info := bindataFileInfo{name: "1561059285_add_whisper_keys.up.sql", size: 112, mode: os.FileMode(0644), modTime: time.Unix(1564484687, 0)}
|
||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x25, 0x41, 0xc, 0x92, 0xdd, 0x9e, 0xff, 0x5d, 0xd0, 0x93, 0xe4, 0x24, 0x50, 0x29, 0xcf, 0xc6, 0xf7, 0x49, 0x3c, 0x73, 0xd9, 0x8c, 0xfa, 0xf2, 0xcf, 0xf6, 0x6f, 0xbc, 0x31, 0xe6, 0xf7, 0xe2}}
|
||||
return a, nil
|
||||
}
|
||||
|
@ -126,7 +126,7 @@ func docGo() (*asset, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "doc.go", size: 373, mode: os.FileMode(0644), modTime: time.Unix(1564400388, 0)}
|
||||
info := bindataFileInfo{name: "doc.go", size: 373, mode: os.FileMode(0644), modTime: time.Unix(1564484687, 0)}
|
||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x23, 0x6a, 0xc1, 0xce, 0x94, 0xf6, 0xef, 0xf1, 0x97, 0x95, 0xb, 0x35, 0xaf, 0x5f, 0xe7, 0x5f, 0xac, 0x6e, 0xb8, 0xab, 0xba, 0xb5, 0x35, 0x97, 0x22, 0x36, 0x11, 0xce, 0x44, 0xfc, 0xfa, 0xac}}
|
||||
return a, nil
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue