Add audio messages

Why make this change?

We are adding support of audio recorded files, similarly to how we did
with images

What has changed?

- Added protobuf definition, only AAC supported
- Added migrations to store files
- Fixed an issue with nil pointer when transaction would fail to be
created, causing the application to crash
This commit is contained in:
Andrea Maria Piana 2020-06-17 20:55:49 +02:00
parent 717ed3e1cf
commit e58ba1e9c8
No known key found for this signature in database
GPG Key ID: AA6CCA6DE0E06424
14 changed files with 451 additions and 45 deletions

BIN
_assets/tests/test.aac Normal file

Binary file not shown.

29
protocol/audio/type.go Normal file
View File

@ -0,0 +1,29 @@
package audio
import (
"github.com/status-im/status-go/protocol/protobuf"
)
func aac(buf []byte) bool {
return len(buf) > 1 &&
((buf[0] == 0xFF && buf[1] == 0xF1) ||
(buf[0] == 0xFF && buf[1] == 0xF9))
}
func amr(buf []byte) bool {
return len(buf) > 11 &&
buf[0] == 0x23 && buf[1] == 0x21 &&
buf[2] == 0x41 && buf[3] == 0x4D &&
buf[4] == 0x52 && buf[5] == 0x0A
}
func Type(buf []byte) protobuf.AudioMessage_AudioType {
switch {
case aac(buf):
return protobuf.AudioMessage_AAC
case amr(buf):
return protobuf.AudioMessage_AMR
default:
return protobuf.AudioMessage_UNKNOWN_AUDIO_TYPE
}
}

View File

@ -22,6 +22,8 @@ type QuotedMessage struct {
Text string `json:"text"`
// Base64Image is the converted base64 image
Base64Image string `json:"image,omitempty"`
// Base64Audio is the converted base64 audio
Base64Audio string `json:"audio,omitempty"`
}
type CommandState int
@ -102,6 +104,10 @@ type Message struct {
Base64Image string `json:"image,omitempty"`
// ImagePath is the path of the image to be sent
ImagePath string `json:"imagePath,omitempty"`
// Base64Audio is the converted base64 audio
Base64Audio string `json:"audio,omitempty"`
// AudioPath is the path of the audio to be sent
AudioPath string `json:"audioPath,omitempty"`
// Replace indicates that this is a replacement of a message
// that has been updated
@ -134,6 +140,7 @@ func (m *Message) MarshalJSON() ([]byte, error) {
ResponseTo string `json:"responseTo"`
EnsName string `json:"ensName"`
Image string `json:"image,omitempty"`
Audio string `json:"audio,omitempty"`
Sticker *StickerAlias `json:"sticker"`
CommandParameters *CommandParameters `json:"commandParameters"`
Timestamp uint64 `json:"timestamp"`
@ -159,6 +166,7 @@ func (m *Message) MarshalJSON() ([]byte, error) {
ResponseTo: m.ResponseTo,
EnsName: m.EnsName,
Image: m.Base64Image,
Audio: m.Base64Audio,
Timestamp: m.Timestamp,
ContentType: m.ContentType,
MessageType: m.MessageType,
@ -239,6 +247,37 @@ func (m *Message) parseImage() error {
return nil
}
// parseAudio check the message contains an audio, and if so
// it creates the a base64 encoded version of it.
func (m *Message) parseAudio() error {
if m.ContentType != protobuf.ChatMessage_AUDIO {
return nil
}
audio := m.GetAudio()
if audio == nil {
return errors.New("audio empty")
}
payload := audio.Payload
e64 := base64.StdEncoding
maxEncLen := e64.EncodedLen(len(payload))
encBuf := make([]byte, maxEncLen)
e64.Encode(encBuf, payload)
mime, err := getAudioMessageMIME(audio)
if err != nil {
return err
}
m.Base64Audio = fmt.Sprintf("data:audio/%s;base64,%s", mime, encBuf)
return nil
}
// PrepareContent return the parsed content of the message, the line-count and whether
// is a right-to-left message
func (m *Message) PrepareContent() error {
@ -250,7 +289,10 @@ func (m *Message) PrepareContent() error {
m.ParsedText = jsonParsedText
m.LineCount = strings.Count(m.Text, "\n")
m.RTL = isRTL(m.Text)
return m.parseImage()
if err := m.parseImage(); err != nil {
return err
}
return m.parseAudio()
}
func getImageMessageMIME(i *protobuf.ImageMessage) (string, error) {
@ -266,3 +308,14 @@ func getImageMessageMIME(i *protobuf.ImageMessage) (string, error) {
}
return "", errors.New("image format not supported")
}
func getAudioMessageMIME(i *protobuf.AudioMessage) (string, error) {
switch i.Type {
case protobuf.AudioMessage_AAC:
return "aac", nil
case protobuf.AudioMessage_AMR:
return "amr", nil
}
return "", errors.New("audio format not supported")
}

View File

@ -36,6 +36,9 @@ func (db sqlitePersistence) tableUserMessagesAllFields() string {
image_payload,
image_type,
image_base64,
audio_payload,
audio_type,
audio_base64,
command_id,
command_value,
command_from,
@ -68,6 +71,7 @@ func (db sqlitePersistence) tableUserMessagesAllFieldsJoin() string {
m1.sticker_pack,
m1.sticker_hash,
m1.image_base64,
m1.audio_base64,
m1.command_id,
m1.command_value,
m1.command_from,
@ -83,6 +87,7 @@ func (db sqlitePersistence) tableUserMessagesAllFieldsJoin() string {
m2.source,
m2.text,
m2.image_base64,
m2.audio_base64,
c.alias,
c.identicon`
}
@ -99,6 +104,7 @@ func (db sqlitePersistence) tableUserMessagesScanAllFields(row scanner, message
var quotedText sql.NullString
var quotedFrom sql.NullString
var quotedImage sql.NullString
var quotedAudio sql.NullString
var alias sql.NullString
var identicon sql.NullString
@ -123,6 +129,7 @@ func (db sqlitePersistence) tableUserMessagesScanAllFields(row scanner, message
&sticker.Pack,
&sticker.Hash,
&message.Base64Image,
&message.Base64Audio,
&command.ID,
&command.Value,
&command.From,
@ -138,6 +145,7 @@ func (db sqlitePersistence) tableUserMessagesScanAllFields(row scanner, message
&quotedFrom,
&quotedText,
&quotedImage,
&quotedAudio,
&alias,
&identicon,
}
@ -151,6 +159,7 @@ func (db sqlitePersistence) tableUserMessagesScanAllFields(row scanner, message
From: quotedFrom.String,
Text: quotedText.String,
Base64Image: quotedImage.String,
Base64Audio: quotedAudio.String,
}
}
message.Alias = alias.String
@ -177,6 +186,11 @@ func (db sqlitePersistence) tableUserMessagesAllValues(message *Message) ([]inte
image = &protobuf.ImageMessage{}
}
audio := message.GetAudio()
if audio == nil {
audio = &protobuf.AudioMessage{}
}
command := message.CommandParameters
if command == nil {
command = &CommandParameters{}
@ -201,6 +215,9 @@ func (db sqlitePersistence) tableUserMessagesAllValues(message *Message) ([]inte
image.Payload,
image.Type,
message.Base64Image,
audio.Payload,
audio.Type,
message.Base64Audio,
command.ID,
command.Value,
command.From,

View File

@ -11,6 +11,7 @@ import (
)
const expectedJPEG = "data:image/jpeg;base64,/9j/2wBDAAMCAgICAgMCAgIDAwMDBAYEBAQEBAgGBgUGCQgKCgkICQkKDA8MCgsOCwkJDRENDg8QEBEQCgwSExIQEw8QEBD/yQALCAABAAEBAREA/8wABgAQEAX/2gAIAQEAAD8A0s8g/9k="
const expectedAAC = "data:audio/aac;base64,//FQgBw//NoATGF2YzUyLjcwLjAAQniptokphEFCg5qs1v9fn48+qz1rfWNhwvz+CqB5dipmq3T2PlT1Ld6sPj+19fUt1C3NKV0KowiqohZVCrdf19WMatvV3YbIvAuy/q2RafA8UiZPmZY7DdmHZtP9ri25kedWSiMKQRt79ttlod55LkuX7/f7/f7/f7/YGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYHNqo8g5qs1v9fn48+qz1rfWNhwvz+CqAAAAAAAAAAAAAAAAAAAAAAABw//FQgCNf/CFXbUZfDKFRgsYlKDegtXJH9eLkT54uRM1ckDYDcXRzZGF6Kz5Yps5fTeLY6w7gclwly+0PJL3udY3PyekTFI65bdniF3OjvHeafzZfWTs0qRMSkdll1sbb4SNT5e8vX98ytot6jEZ0NhJi2pBVP/tKV2JMyo36n9uxR2tKR+FoLCsP4SVi49kmvaSCWm5bQD96OmVQA9Q40bqnOa7rT8j9N0TlK991XdcenGTLbyS6eUnN2U1ckf14uRPni5EzVyQAAAAAAAAAAx6Q1flBp+KH2LhgH2Xx+14QB2/jcizm6ngck4vB9DoH9/Vcb7E8Dy+D/1ii1pSPwsUUUXCSsXHsk17SBfKwn2uHr6QAAAAAAAHA//FQgBt//CF3VO1KFCFWcd/r04m+O0758/tXHUlvaqEK9lvhUZXEZMXKMV/LQ6B3/mOl/Mrfs6jpD2b7f+n4yt+tm2x5ZmnpD++dZo/V9VgblI3OW/s1b8qt0h1RBiIRIIYIYQIBeCM8yy7etkwt1JAajRSoZGwwNZ07TTFTyMR1mTUVVUTW97vaDaHU5DV1snBf0mN4fraa+rf/vpdZ8FxqatGjNxPh35UuVfpNqc48W4nZ6rOO/16cTfHad8+f2rjqS3tVAAAAAAAAAAAAAAAAAAAAAAAAAAAO//FQgBm//CEXVPU+GiFsPr7x6+N6v+m+q511I4SgtYVyoyWjcMWMxkaxxDGSx1qVcarjDESt8zLQehx/lkil/GrHBy/NfJcHek0XtfanZJLHNXO2rUnFklPAlQSBS4l0pIoXIfORcXx0UYj1nTsSe1/0wXDkkFCfxWHtqRayOmWm3oS6JGdnZdtjesjByefiS8dLW1tVVVC58ijoxN3gmGFYj07+YJ6eth9fePXxvV/031XOupHCUAAAAAAAAAAAAAAAAAAAAAAAAAAA4P/xUIAcf/whN1T9NsMOEK5rxxxxXnid+f0/Ia195vi6oGH1ZVr6kjqScdSF9lt3qXH+Lxf0fo/Oe53r99IUPzybv/YWGZ7Vgk31MGw+DMp05+3y9fPERUTHlt1c9sUyoqCaD5bdXVz2wkG0hnpDmFy8r0fr3VBn/C7Rmg+L0/45EWfdocGq3HQ1uRro0GJK+vsvo837NR82s01l/n97rsWn7RYNBM3WRcDY3cJKosqMJhgdHtj9yflthd65rxxxxXnid+f0/Ia195vi6oAAAAAAAAAAAAAAAAAAAAAAAAAAAABw"
func TestPrepareContentImage(t *testing.T) {
file, err := os.Open("../_assets/tests/test.jpg")
@ -29,7 +30,7 @@ func TestPrepareContentImage(t *testing.T) {
message.Payload = &protobuf.ChatMessage_Image{Image: &image}
require.NoError(t, message.PrepareContent())
require.Equal(t, message.Base64Image, expectedJPEG)
require.Equal(t, expectedJPEG, message.Base64Image)
}
func TestGetImageMessageMIME(t *testing.T) {
@ -57,3 +58,39 @@ func TestGetImageMessageMIME(t *testing.T) {
_, err = getImageMessageMIME(unknown)
require.Error(t, err)
}
func TestPrepareContentAudio(t *testing.T) {
file, err := os.Open("../_assets/tests/test.aac")
require.NoError(t, err)
defer file.Close()
payload, err := ioutil.ReadAll(file)
require.NoError(t, err)
message := &Message{}
message.ContentType = protobuf.ChatMessage_AUDIO
audio := protobuf.AudioMessage{
Payload: payload,
Type: protobuf.AudioMessage_AAC,
}
message.Payload = &protobuf.ChatMessage_Audio{Audio: &audio}
require.NoError(t, message.PrepareContent())
require.Equal(t, expectedAAC, message.Base64Audio)
}
func TestGetAudioMessageMIME(t *testing.T) {
aac := &protobuf.AudioMessage{Type: protobuf.AudioMessage_AAC}
mime, err := getAudioMessageMIME(aac)
require.NoError(t, err)
require.Equal(t, "aac", mime)
amr := &protobuf.AudioMessage{Type: protobuf.AudioMessage_AMR}
mime, err = getAudioMessageMIME(amr)
require.NoError(t, err)
require.Equal(t, "amr", mime)
unknown := &protobuf.ImageMessage{Type: protobuf.ImageMessage_UNKNOWN_IMAGE_TYPE}
_, err = getImageMessageMIME(unknown)
require.Error(t, err)
}

View File

@ -209,5 +209,22 @@ func ValidateReceivedChatMessage(message *protobuf.ChatMessage, whisperTimestamp
}
}
if message.ContentType == protobuf.ChatMessage_AUDIO {
if message.Payload == nil {
return errors.New("no audio content")
}
audio := message.GetAudio()
if audio == nil {
return errors.New("no audio content")
}
if len(audio.Payload) == 0 {
return errors.New("audio payload empty")
}
if audio.Type == protobuf.AudioMessage_UNKNOWN_AUDIO_TYPE {
return errors.New("audio type unknown")
}
}
return nil
}

View File

@ -402,6 +402,68 @@ func (s *MessageValidatorSuite) TestValidatePlainTextMessage() {
ContentType: protobuf.ChatMessage_IMAGE,
},
},
{
Name: "Valid audio message",
WhisperTimestamp: 2,
Valid: true,
Message: protobuf.ChatMessage{
ChatId: "a",
Text: "valid",
Clock: 2,
Timestamp: 3,
ResponseTo: "",
EnsName: "",
Payload: &protobuf.ChatMessage_Audio{
Audio: &protobuf.AudioMessage{
Type: 1,
Payload: []byte("some-payload"),
},
},
MessageType: protobuf.ChatMessage_ONE_TO_ONE,
ContentType: protobuf.ChatMessage_AUDIO,
},
},
{
Name: "Invalid audio message, type unknown",
WhisperTimestamp: 2,
Valid: false,
Message: protobuf.ChatMessage{
ChatId: "a",
Text: "valid",
Clock: 2,
Timestamp: 3,
ResponseTo: "",
EnsName: "",
Payload: &protobuf.ChatMessage_Audio{
Audio: &protobuf.AudioMessage{
Type: protobuf.AudioMessage_UNKNOWN_AUDIO_TYPE,
Payload: []byte("some-payload"),
},
},
MessageType: protobuf.ChatMessage_ONE_TO_ONE,
ContentType: protobuf.ChatMessage_STICKER,
},
},
{
Name: "Invalid audio message, missing payload",
WhisperTimestamp: 2,
Valid: false,
Message: protobuf.ChatMessage{
ChatId: "a",
Text: "valid",
Clock: 2,
Timestamp: 3,
ResponseTo: "",
EnsName: "",
Payload: &protobuf.ChatMessage_Audio{
Audio: &protobuf.AudioMessage{
Type: 1,
},
},
MessageType: protobuf.ChatMessage_ONE_TO_ONE,
ContentType: protobuf.ChatMessage_AUDIO,
},
},
}
for _, tc := range testCases {

View File

@ -19,6 +19,7 @@ import (
"github.com/status-im/status-go/eth-node/crypto"
"github.com/status-im/status-go/eth-node/types"
enstypes "github.com/status-im/status-go/eth-node/types/ens"
"github.com/status-im/status-go/protocol/audio"
"github.com/status-im/status-go/protocol/common"
"github.com/status-im/status-go/protocol/encryption"
"github.com/status-im/status-go/protocol/encryption/multidevice"
@ -1382,6 +1383,26 @@ func (m *Messenger) SendChatMessage(ctx context.Context, message *Message) (*Mes
message.Payload = &protobuf.ChatMessage_Image{Image: &image}
}
if len(message.AudioPath) != 0 {
file, err := os.Open(message.AudioPath)
if err != nil {
return nil, err
}
defer file.Close()
payload, err := ioutil.ReadAll(file)
if err != nil {
return nil, err
}
audio := protobuf.AudioMessage{
Payload: payload,
Type: audio.Type(payload),
}
message.Payload = &protobuf.ChatMessage_Audio{Audio: &audio}
}
logger := m.logger.With(zap.String("site", "Send"), zap.String("chatID", message.ChatId))
var response MessengerResponse

View File

@ -14,6 +14,8 @@
// 1591277220_add_index_messages.up.sql (240B)
// 1593087212_add_mute_chat_and_raw_message_fields.down.sql (0)
// 1593087212_add_mute_chat_and_raw_message_fields.up.sql (215B)
// 1595862781_add_audio_data.down.sql (0)
// 1595862781_add_audio_data.up.sql (186B)
// doc.go (850B)
package migrations
@ -338,7 +340,7 @@ func _1593087212_add_mute_chat_and_raw_message_fieldsDownSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1593087212_add_mute_chat_and_raw_message_fields.down.sql", size: 0, mode: os.FileMode(0644), modTime: time.Unix(1595832279, 0)}
info := bindataFileInfo{name: "1593087212_add_mute_chat_and_raw_message_fields.down.sql", size: 0, mode: os.FileMode(0644), modTime: time.Unix(1595862768, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14, 0x9a, 0xfb, 0xf4, 0xc8, 0x99, 0x6f, 0xb9, 0x24, 0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b, 0x93, 0x4c, 0xa4, 0x95, 0x99, 0x1b, 0x78, 0x52, 0xb8, 0x55}}
return a, nil
}
@ -358,11 +360,51 @@ 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(1595832279, 0)}
info := bindataFileInfo{name: "1593087212_add_mute_chat_and_raw_message_fields.up.sql", size: 215, mode: os.FileMode(0644), modTime: time.Unix(1595862768, 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}}
return a, nil
}
var __1595862781_add_audio_dataDownSql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x01\x00\x00\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00")
func _1595862781_add_audio_dataDownSqlBytes() ([]byte, error) {
return bindataRead(
__1595862781_add_audio_dataDownSql,
"1595862781_add_audio_data.down.sql",
)
}
func _1595862781_add_audio_dataDownSql() (*asset, error) {
bytes, err := _1595862781_add_audio_dataDownSqlBytes()
if err != nil {
return nil, err
}
info := bindataFileInfo{name: "1595862781_add_audio_data.down.sql", size: 0, mode: os.FileMode(0644), modTime: time.Unix(1595862768, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14, 0x9a, 0xfb, 0xf4, 0xc8, 0x99, 0x6f, 0xb9, 0x24, 0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b, 0x93, 0x4c, 0xa4, 0x95, 0x99, 0x1b, 0x78, 0x52, 0xb8, 0x55}}
return a, nil
}
var __1595862781_add_audio_dataUpSql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x94\xcb\x41\x0a\xc2\x40\x0c\x05\xd0\x7d\x4f\xf1\xe9\x19\xc4\x4d\x57\x33\x4e\x04\x21\x66\x40\x32\xe0\xae\x44\x1a\x44\x50\x5a\x8c\x5d\xf4\xf6\x9e\xa1\x07\x78\x89\x95\x6e\xd0\x94\x99\xb0\x86\x7f\xc7\x8f\x47\xd8\xd3\x03\xa9\x14\x9c\x2a\xb7\xab\xc0\xd6\xe9\x35\x8f\x8b\x6d\xef\xd9\x26\x64\xae\x79\xe8\x76\xc0\xdf\xb6\x38\x2e\xa2\xbb\xd0\xc3\xc2\x8f\x07\x28\xdd\x15\x52\x15\xd2\x98\x51\xe8\x9c\x1a\x2b\xfa\x7e\xe8\xfe\x01\x00\x00\xff\xff\x87\xd0\x8b\xbb\xba\x00\x00\x00")
func _1595862781_add_audio_dataUpSqlBytes() ([]byte, error) {
return bindataRead(
__1595862781_add_audio_dataUpSql,
"1595862781_add_audio_data.up.sql",
)
}
func _1595862781_add_audio_dataUpSql() (*asset, error) {
bytes, err := _1595862781_add_audio_dataUpSqlBytes()
if err != nil {
return nil, err
}
info := bindataFileInfo{name: "1595862781_add_audio_data.up.sql", size: 186, mode: os.FileMode(0644), modTime: time.Unix(1595862768, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x49, 0xf0, 0xf7, 0x5d, 0x8e, 0xc4, 0xd5, 0xb1, 0xb0, 0xa, 0x7c, 0xbd, 0x26, 0x22, 0xfe, 0xc0, 0xf0, 0xb0, 0x4e, 0x97, 0x2f, 0xcf, 0x8c, 0x24, 0x88, 0xdc, 0x6e, 0xab, 0x41, 0x99, 0xce, 0x36}}
return a, nil
}
var _docGo = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x84\x52\x3f\x8f\xdb\x3e\x0c\xdd\xf3\x29\x1e\x6e\xb9\xe5\x22\x07\xf8\xfd\xa6\xdb\x3a\x74\xe8\xd2\x2e\xd9\x0b\x46\xa6\x6d\x22\x32\xe5\x8a\xf4\x39\xf9\xf6\x85\x74\x17\x9c\x51\x14\xe8\x4a\x89\x8f\xef\x5f\xd7\xe1\x3c\x89\x61\x90\xc4\x10\x83\x72\x64\x33\x2a\x77\x5c\x38\xd2\x6a\x8c\xa7\x51\x7c\x5a\x2f\x21\xe6\xb9\x33\x27\x5f\xed\x28\x73\x37\xcb\x58\xc8\xb9\x7b\xfb\xff\xe9\xd0\x75\x88\xa4\xcf\x8e\x89\xb4\x4f\xdc\xb0\x0c\xe6\x54\x5c\x74\xc4\x26\x3e\x81\xb0\x14\x1e\xe4\x16\xf0\xc5\x91\x98\xcc\xe1\x13\xf9\xb3\xc1\x27\x46\x24\xe3\x0a\x33\xe4\x82\x31\x1f\x2f\xa2\x3d\x39\x85\x3a\xfa\x36\xec\x26\x95\x61\xa4\x94\xb8\xc7\x50\xf2\xdc\x76\x8d\x66\x46\x2f\x85\xa3\xe7\x72\x7f\x01\x99\xb1\x43\x69\x66\xab\xfb\x13\xbd\x31\x34\x7f\x9c\x07\x69\xff\x6f\x45\xd8\x72\xb9\x1a\xc8\xc0\xb7\x85\xa3\x73\x1f\x0e\x15\xeb\xfb\x8f\xf3\xd7\x57\x9c\x27\xae\xf0\x55\x5a\x1e\x1a\x85\x66\x9e\x32\xf7\x06\xcf\x18\x72\x4a\x79\x6b\x0f\xab\xca\x0d\x2e\x33\x9b\xd3\xbc\x20\x66\x7d\x63\x75\xc9\x5a\xd1\x56\x4d\x72\xe5\xf6\xcf\xb7\x0c\x51\x71\xa1\xf4\xee\x5e\x93\x7e\x7e\x37\xe8\x11\x44\x5c\x4b\x61\xf5\x74\x6f\x2b\xac\xb1\xdc\x97\x8a\x85\x77\xe6\x92\xd5\x9a\xbc\xa5\x64\xcf\x31\xa7\xdd\xbc\xa2\xd9\x44\x85\x3f\x1d\x73\xba\x24\x7e\xc1\x36\x49\x9c\x30\x33\xa9\xb5\x40\xda\x87\x44\xce\xe6\x9f\xfb\x10\x85\x73\x99\xad\x0a\xae\xfc\xaa\xbb\x15\xb3\x16\xe7\x91\xc3\x8e\x50\x33\x7f\xa1\xf8\x51\x85\xc7\x95\xd5\xd8\x40\x7f\x98\xf2\x08\x79\x63\x50\xdf\xe3\x74\x3a\x9d\xfe\xfb\x19\x42\x68\x5d\xe0\x1b\xcd\x4b\xa5\xe9\xb5\xa3\x9b\xa4\x84\x0b\x43\x46\xcd\x85\xfb\xca\x8a\x6f\x62\xad\x64\x31\x09\xab\xd7\xcc\x2a\x5e\x4e\x3d\x97\xaa\x47\xf7\x7a\xfe\x66\x59\x38\x1c\x16\x8a\x57\x1a\x19\xf6\x2b\x89\x73\x0d\x7a\xcc\xaf\x23\x2b\xd7\x3a\xec\xcb\x77\x5c\xae\xe3\xde\xec\x63\x46\x08\xdd\xe7\x20\x8c\x19\xe1\xf0\x3b\x00\x00\xff\xff\x12\xcd\x7f\xc4\x52\x03\x00\x00")
func docGoBytes() ([]byte, error) {
@ -502,6 +544,10 @@ var _bindata = map[string]func() (*asset, error){
"1593087212_add_mute_chat_and_raw_message_fields.up.sql": _1593087212_add_mute_chat_and_raw_message_fieldsUpSql,
"1595862781_add_audio_data.down.sql": _1595862781_add_audio_dataDownSql,
"1595862781_add_audio_data.up.sql": _1595862781_add_audio_dataUpSql,
"doc.go": docGo,
}
@ -560,7 +606,9 @@ var _bintree = &bintree{nil, map[string]*bintree{
"1591277220_add_index_messages.up.sql": &bintree{_1591277220_add_index_messagesUpSql, map[string]*bintree{}},
"1593087212_add_mute_chat_and_raw_message_fields.down.sql": &bintree{_1593087212_add_mute_chat_and_raw_message_fieldsDownSql, map[string]*bintree{}},
"1593087212_add_mute_chat_and_raw_message_fields.up.sql": &bintree{_1593087212_add_mute_chat_and_raw_message_fieldsUpSql, map[string]*bintree{}},
"doc.go": &bintree{docGo, map[string]*bintree{}},
"1595862781_add_audio_data.down.sql": &bintree{_1595862781_add_audio_dataDownSql, map[string]*bintree{}},
"1595862781_add_audio_data.up.sql": &bintree{_1595862781_add_audio_dataUpSql, map[string]*bintree{}},
"doc.go": &bintree{docGo, map[string]*bintree{}},
}}
// RestoreAsset restores an asset under the given directory.

View File

@ -0,0 +1,3 @@
ALTER TABLE user_messages ADD COLUMN audio_payload BLOB;
ALTER TABLE user_messages ADD COLUMN audio_type INT;
ALTER TABLE user_messages ADD COLUMN audio_base64 TEXT NOT NULL DEFAULT "";

View File

@ -32,6 +32,10 @@ func (db sqlitePersistence) SaveChat(chat Chat) error {
func (db sqlitePersistence) SaveChats(chats []*Chat) error {
tx, err := db.db.BeginTx(context.Background(), &sql.TxOptions{})
if err != nil {
return err
}
defer func() {
if err == nil {
err = tx.Commit()
@ -52,6 +56,10 @@ func (db sqlitePersistence) SaveChats(chats []*Chat) error {
func (db sqlitePersistence) SaveContacts(contacts []*Contact) error {
tx, err := db.db.BeginTx(context.Background(), &sql.TxOptions{})
if err != nil {
return err
}
defer func() {
if err == nil {
err = tx.Commit()

View File

@ -54,6 +54,34 @@ func (ImageMessage_ImageType) EnumDescriptor() ([]byte, []int) {
return fileDescriptor_263952f55fd35689, []int{1, 0}
}
type AudioMessage_AudioType int32
const (
AudioMessage_UNKNOWN_AUDIO_TYPE AudioMessage_AudioType = 0
AudioMessage_AAC AudioMessage_AudioType = 1
AudioMessage_AMR AudioMessage_AudioType = 2
)
var AudioMessage_AudioType_name = map[int32]string{
0: "UNKNOWN_AUDIO_TYPE",
1: "AAC",
2: "AMR",
}
var AudioMessage_AudioType_value = map[string]int32{
"UNKNOWN_AUDIO_TYPE": 0,
"AAC": 1,
"AMR": 2,
}
func (x AudioMessage_AudioType) String() string {
return proto.EnumName(AudioMessage_AudioType_name, int32(x))
}
func (AudioMessage_AudioType) EnumDescriptor() ([]byte, []int) {
return fileDescriptor_263952f55fd35689, []int{2, 0}
}
type ChatMessage_MessageType int32
const (
@ -86,7 +114,7 @@ func (x ChatMessage_MessageType) String() string {
}
func (ChatMessage_MessageType) EnumDescriptor() ([]byte, []int) {
return fileDescriptor_263952f55fd35689, []int{2, 0}
return fileDescriptor_263952f55fd35689, []int{3, 0}
}
type ChatMessage_ContentType int32
@ -101,6 +129,7 @@ const (
// Only local
ChatMessage_SYSTEM_MESSAGE_CONTENT_PRIVATE_GROUP ChatMessage_ContentType = 6
ChatMessage_IMAGE ChatMessage_ContentType = 7
ChatMessage_AUDIO ChatMessage_ContentType = 8
)
var ChatMessage_ContentType_name = map[int32]string{
@ -112,6 +141,7 @@ var ChatMessage_ContentType_name = map[int32]string{
5: "TRANSACTION_COMMAND",
6: "SYSTEM_MESSAGE_CONTENT_PRIVATE_GROUP",
7: "IMAGE",
8: "AUDIO",
}
var ChatMessage_ContentType_value = map[string]int32{
@ -123,6 +153,7 @@ var ChatMessage_ContentType_value = map[string]int32{
"TRANSACTION_COMMAND": 5,
"SYSTEM_MESSAGE_CONTENT_PRIVATE_GROUP": 6,
"IMAGE": 7,
"AUDIO": 8,
}
func (x ChatMessage_ContentType) String() string {
@ -130,7 +161,7 @@ func (x ChatMessage_ContentType) String() string {
}
func (ChatMessage_ContentType) EnumDescriptor() ([]byte, []int) {
return fileDescriptor_263952f55fd35689, []int{2, 1}
return fileDescriptor_263952f55fd35689, []int{3, 1}
}
type StickerMessage struct {
@ -227,6 +258,53 @@ func (m *ImageMessage) GetType() ImageMessage_ImageType {
return ImageMessage_UNKNOWN_IMAGE_TYPE
}
type AudioMessage struct {
Payload []byte `protobuf:"bytes,1,opt,name=payload,proto3" json:"payload,omitempty"`
Type AudioMessage_AudioType `protobuf:"varint,2,opt,name=type,proto3,enum=protobuf.AudioMessage_AudioType" json:"type,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *AudioMessage) Reset() { *m = AudioMessage{} }
func (m *AudioMessage) String() string { return proto.CompactTextString(m) }
func (*AudioMessage) ProtoMessage() {}
func (*AudioMessage) Descriptor() ([]byte, []int) {
return fileDescriptor_263952f55fd35689, []int{2}
}
func (m *AudioMessage) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_AudioMessage.Unmarshal(m, b)
}
func (m *AudioMessage) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_AudioMessage.Marshal(b, m, deterministic)
}
func (m *AudioMessage) XXX_Merge(src proto.Message) {
xxx_messageInfo_AudioMessage.Merge(m, src)
}
func (m *AudioMessage) XXX_Size() int {
return xxx_messageInfo_AudioMessage.Size(m)
}
func (m *AudioMessage) XXX_DiscardUnknown() {
xxx_messageInfo_AudioMessage.DiscardUnknown(m)
}
var xxx_messageInfo_AudioMessage proto.InternalMessageInfo
func (m *AudioMessage) GetPayload() []byte {
if m != nil {
return m.Payload
}
return nil
}
func (m *AudioMessage) GetType() AudioMessage_AudioType {
if m != nil {
return m.Type
}
return AudioMessage_UNKNOWN_AUDIO_TYPE
}
type ChatMessage struct {
// Lamport timestamp of the chat message
Clock uint64 `protobuf:"varint,1,opt,name=clock,proto3" json:"clock,omitempty"`
@ -251,6 +329,7 @@ type ChatMessage struct {
// Types that are valid to be assigned to Payload:
// *ChatMessage_Sticker
// *ChatMessage_Image
// *ChatMessage_Audio
Payload isChatMessage_Payload `protobuf_oneof:"payload"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
@ -261,7 +340,7 @@ func (m *ChatMessage) Reset() { *m = ChatMessage{} }
func (m *ChatMessage) String() string { return proto.CompactTextString(m) }
func (*ChatMessage) ProtoMessage() {}
func (*ChatMessage) Descriptor() ([]byte, []int) {
return fileDescriptor_263952f55fd35689, []int{2}
return fileDescriptor_263952f55fd35689, []int{3}
}
func (m *ChatMessage) XXX_Unmarshal(b []byte) error {
@ -350,10 +429,16 @@ type ChatMessage_Image struct {
Image *ImageMessage `protobuf:"bytes,10,opt,name=image,proto3,oneof"`
}
type ChatMessage_Audio struct {
Audio *AudioMessage `protobuf:"bytes,11,opt,name=audio,proto3,oneof"`
}
func (*ChatMessage_Sticker) isChatMessage_Payload() {}
func (*ChatMessage_Image) isChatMessage_Payload() {}
func (*ChatMessage_Audio) isChatMessage_Payload() {}
func (m *ChatMessage) GetPayload() isChatMessage_Payload {
if m != nil {
return m.Payload
@ -375,61 +460,75 @@ func (m *ChatMessage) GetImage() *ImageMessage {
return nil
}
func (m *ChatMessage) GetAudio() *AudioMessage {
if x, ok := m.GetPayload().(*ChatMessage_Audio); ok {
return x.Audio
}
return nil
}
// XXX_OneofWrappers is for the internal use of the proto package.
func (*ChatMessage) XXX_OneofWrappers() []interface{} {
return []interface{}{
(*ChatMessage_Sticker)(nil),
(*ChatMessage_Image)(nil),
(*ChatMessage_Audio)(nil),
}
}
func init() {
proto.RegisterEnum("protobuf.ImageMessage_ImageType", ImageMessage_ImageType_name, ImageMessage_ImageType_value)
proto.RegisterEnum("protobuf.AudioMessage_AudioType", AudioMessage_AudioType_name, AudioMessage_AudioType_value)
proto.RegisterEnum("protobuf.ChatMessage_MessageType", ChatMessage_MessageType_name, ChatMessage_MessageType_value)
proto.RegisterEnum("protobuf.ChatMessage_ContentType", ChatMessage_ContentType_name, ChatMessage_ContentType_value)
proto.RegisterType((*StickerMessage)(nil), "protobuf.StickerMessage")
proto.RegisterType((*ImageMessage)(nil), "protobuf.ImageMessage")
proto.RegisterType((*AudioMessage)(nil), "protobuf.AudioMessage")
proto.RegisterType((*ChatMessage)(nil), "protobuf.ChatMessage")
}
func init() { proto.RegisterFile("chat_message.proto", fileDescriptor_263952f55fd35689) }
var fileDescriptor_263952f55fd35689 = []byte{
// 569 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x7c, 0x53, 0xcd, 0x4e, 0xdb, 0x40,
0x10, 0xc6, 0x89, 0x1d, 0x27, 0xe3, 0x14, 0x6d, 0xa7, 0x08, 0x5c, 0x09, 0xa9, 0x69, 0xd4, 0x43,
0x4e, 0x39, 0x50, 0x0e, 0xbd, 0x9a, 0xb0, 0x0d, 0x06, 0xfc, 0xa3, 0xf5, 0xa6, 0x94, 0x93, 0x65,
0xcc, 0x96, 0x44, 0xe0, 0xd8, 0xc2, 0x5b, 0xa9, 0x5c, 0xfa, 0x3e, 0x7d, 0x9e, 0xbe, 0x50, 0xe5,
0x35, 0x26, 0x4e, 0x55, 0xf5, 0xb4, 0xf3, 0xfb, 0xed, 0xb7, 0xdf, 0xcc, 0x02, 0xa6, 0xcb, 0x44,
0xc6, 0x99, 0x28, 0xcb, 0xe4, 0x4e, 0x4c, 0x8b, 0xc7, 0x5c, 0xe6, 0xd8, 0x57, 0xc7, 0xcd, 0xf7,
0x6f, 0xe3, 0x4f, 0xb0, 0x1b, 0xc9, 0x55, 0x7a, 0x2f, 0x1e, 0xbd, 0xba, 0x02, 0x11, 0xf4, 0x65,
0x52, 0x2e, 0x6d, 0x6d, 0xa4, 0x4d, 0x06, 0x4c, 0xd9, 0x55, 0xac, 0x48, 0xd2, 0x7b, 0xbb, 0x33,
0xd2, 0x26, 0x06, 0x53, 0xf6, 0xf8, 0x97, 0x06, 0x43, 0x37, 0x4b, 0xee, 0x44, 0xd3, 0x68, 0x83,
0x59, 0x24, 0x4f, 0x0f, 0x79, 0x72, 0xab, 0x7a, 0x87, 0xac, 0x71, 0xf1, 0x18, 0x74, 0xf9, 0x54,
0x08, 0xd5, 0xbe, 0x7b, 0x34, 0x9a, 0x36, 0xb7, 0x4f, 0xdb, 0xfd, 0xb5, 0xc3, 0x9f, 0x0a, 0xc1,
0x54, 0xf5, 0xd8, 0x85, 0xc1, 0x4b, 0x08, 0xf7, 0x01, 0x17, 0xfe, 0x85, 0x1f, 0x5c, 0xf9, 0xb1,
0xeb, 0x39, 0x73, 0x1a, 0xf3, 0xeb, 0x90, 0x92, 0x1d, 0x34, 0xa1, 0x1b, 0xfa, 0x73, 0xa2, 0x61,
0x1f, 0xf4, 0xf3, 0x90, 0xce, 0x49, 0xa7, 0xb2, 0xae, 0xe8, 0x49, 0x48, 0xba, 0x55, 0x72, 0xee,
0x7e, 0x26, 0xfa, 0xf8, 0xb7, 0x01, 0xd6, 0x6c, 0x99, 0xc8, 0x86, 0xea, 0x1e, 0x18, 0xe9, 0x43,
0x9e, 0xde, 0x2b, 0xa2, 0x3a, 0xab, 0x1d, 0x3c, 0x84, 0x81, 0x5c, 0x65, 0xa2, 0x94, 0x49, 0x56,
0x28, 0xae, 0x3a, 0xdb, 0x04, 0x2a, 0x0d, 0xa4, 0xf8, 0x21, 0xed, 0x6e, 0xad, 0x4b, 0x65, 0xe3,
0x3b, 0xb0, 0x1e, 0x45, 0x59, 0xe4, 0xeb, 0x52, 0xc4, 0x32, 0xb7, 0x75, 0x95, 0x82, 0x26, 0xc4,
0x73, 0x7c, 0x0b, 0x7d, 0xb1, 0x2e, 0xe3, 0x75, 0x92, 0x09, 0xdb, 0x50, 0x59, 0x53, 0xac, 0x4b,
0x3f, 0xc9, 0x04, 0x1e, 0x80, 0xa9, 0x26, 0xb3, 0xba, 0xb5, 0x7b, 0x2a, 0xd3, 0xab, 0x5c, 0xf7,
0x16, 0x4f, 0x61, 0xf8, 0x3c, 0xad, 0x58, 0xa9, 0x66, 0x2a, 0xd5, 0xde, 0x6f, 0x54, 0x6b, 0xbd,
0x64, 0xfa, 0x7c, 0x2a, 0xd9, 0xac, 0x6c, 0xe3, 0x54, 0x28, 0x69, 0xbe, 0x96, 0x62, 0x2d, 0x6b,
0x94, 0xfe, 0xff, 0x50, 0x66, 0x75, 0x65, 0x8d, 0x92, 0x6e, 0x1c, 0x3c, 0x06, 0xb3, 0xac, 0xd7,
0xc3, 0x1e, 0x8c, 0xb4, 0x89, 0x75, 0x64, 0x6f, 0x00, 0xb6, 0xf7, 0xe6, 0x6c, 0x87, 0x35, 0xa5,
0x38, 0x05, 0x63, 0x55, 0x4d, 0xce, 0x06, 0xd5, 0xb3, 0xff, 0xef, 0x81, 0x9f, 0xed, 0xb0, 0xba,
0x6c, 0xfc, 0x13, 0xac, 0xd6, 0x3b, 0xd0, 0x86, 0xbd, 0x66, 0xd6, 0x1e, 0x8d, 0xa2, 0xd6, 0xb4,
0x77, 0x01, 0x02, 0x9f, 0xc6, 0x3c, 0x88, 0x03, 0x9f, 0x12, 0x0d, 0x09, 0x0c, 0xc3, 0xc5, 0xc9,
0xa5, 0x3b, 0x8b, 0xe7, 0x2c, 0x58, 0x84, 0xa4, 0x83, 0xaf, 0xe1, 0x55, 0xc8, 0xdc, 0x2f, 0x0e,
0xa7, 0xcf, 0xa1, 0x2e, 0x8e, 0xe0, 0x30, 0xba, 0x8e, 0x38, 0xf5, 0x5e, 0xd0, 0xb6, 0x2b, 0xf4,
0x6a, 0x95, 0xad, 0x96, 0x04, 0x6d, 0x02, 0xb3, 0xc0, 0xe7, 0xd4, 0xe7, 0x2d, 0x02, 0x9c, 0x7e,
0xe5, 0x71, 0x78, 0xe9, 0xb8, 0x3e, 0xd1, 0xd0, 0x02, 0x33, 0xe2, 0xee, 0xec, 0x82, 0x32, 0xd2,
0x41, 0x80, 0x5e, 0xc4, 0x1d, 0xbe, 0x88, 0x48, 0x17, 0x07, 0x60, 0x50, 0x2f, 0x38, 0x77, 0x89,
0x8e, 0x07, 0xf0, 0x86, 0x33, 0xc7, 0x8f, 0x9c, 0x19, 0x77, 0x83, 0x0a, 0xd1, 0xf3, 0x1c, 0xff,
0x94, 0x18, 0x38, 0x81, 0x0f, 0x7f, 0x11, 0x6b, 0x6e, 0xdb, 0x26, 0xd8, 0xab, 0xd0, 0xd4, 0xd6,
0x13, 0xf3, 0x64, 0xf0, 0xf2, 0xcb, 0x6e, 0x7a, 0x4a, 0xd6, 0x8f, 0x7f, 0x02, 0x00, 0x00, 0xff,
0xff, 0x1f, 0xf2, 0x87, 0xb6, 0xe2, 0x03, 0x00, 0x00,
// 631 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x53, 0xcb, 0x4e, 0xdb, 0x40,
0x14, 0xc5, 0x89, 0x13, 0xc7, 0xd7, 0x29, 0x9a, 0x4e, 0x11, 0xb8, 0x12, 0x52, 0x53, 0xab, 0x8b,
0xac, 0xb2, 0xa0, 0x54, 0xea, 0xd6, 0x98, 0x69, 0x30, 0xe0, 0x87, 0xc6, 0x93, 0x52, 0x56, 0x96,
0x71, 0xa6, 0x24, 0x02, 0xc7, 0x16, 0x36, 0x52, 0xd9, 0xf4, 0x13, 0xba, 0xeb, 0x47, 0x74, 0xdf,
0x0f, 0xac, 0x66, 0x8c, 0x13, 0x83, 0xaa, 0x6e, 0xba, 0xf2, 0x7d, 0x9c, 0x7b, 0x7c, 0xe6, 0xce,
0x19, 0xc0, 0xe9, 0x22, 0xa9, 0xe2, 0x8c, 0x97, 0x65, 0x72, 0xcd, 0x27, 0xc5, 0x5d, 0x5e, 0xe5,
0x78, 0x20, 0x3f, 0x57, 0xf7, 0x5f, 0xad, 0x8f, 0xb0, 0x1d, 0x55, 0xcb, 0xf4, 0x86, 0xdf, 0x79,
0x35, 0x02, 0x63, 0x50, 0x17, 0x49, 0xb9, 0x30, 0x95, 0x91, 0x32, 0xd6, 0xa9, 0x8c, 0x45, 0xad,
0x48, 0xd2, 0x1b, 0xb3, 0x33, 0x52, 0xc6, 0x3d, 0x2a, 0x63, 0xeb, 0x97, 0x02, 0x43, 0x37, 0x4b,
0xae, 0x79, 0x33, 0x68, 0x82, 0x56, 0x24, 0x0f, 0xb7, 0x79, 0x32, 0x97, 0xb3, 0x43, 0xda, 0xa4,
0xf8, 0x10, 0xd4, 0xea, 0xa1, 0xe0, 0x72, 0x7c, 0xfb, 0x60, 0x34, 0x69, 0xfe, 0x3e, 0x69, 0xcf,
0xd7, 0x09, 0x7b, 0x28, 0x38, 0x95, 0x68, 0xcb, 0x05, 0x7d, 0x5d, 0xc2, 0xbb, 0x80, 0x67, 0xfe,
0x99, 0x1f, 0x5c, 0xf8, 0xb1, 0xeb, 0xd9, 0x53, 0x12, 0xb3, 0xcb, 0x90, 0xa0, 0x2d, 0xac, 0x41,
0x37, 0xf4, 0xa7, 0x48, 0xc1, 0x03, 0x50, 0x4f, 0x43, 0x32, 0x45, 0x1d, 0x11, 0x5d, 0x90, 0xa3,
0x10, 0x75, 0x45, 0x73, 0xea, 0x7e, 0x42, 0xaa, 0xf5, 0x53, 0x81, 0xa1, 0x7d, 0x3f, 0x5f, 0xe6,
0xff, 0xa1, 0xb5, 0x3d, 0x5f, 0x27, 0x2d, 0xad, 0x1f, 0x40, 0x5f, 0x97, 0xda, 0x5a, 0xed, 0xd9,
0xb1, 0x1b, 0xb4, 0xb4, 0xda, 0xb6, 0x83, 0x14, 0x19, 0x78, 0x14, 0x75, 0xac, 0x1f, 0x7d, 0x30,
0x9c, 0x45, 0x52, 0x35, 0xb2, 0x76, 0xa0, 0x97, 0xde, 0xe6, 0xe9, 0x8d, 0x14, 0xa5, 0xd2, 0x3a,
0xc1, 0xfb, 0xa0, 0x57, 0xcb, 0x8c, 0x97, 0x55, 0x92, 0x15, 0x52, 0x97, 0x4a, 0x37, 0x05, 0x71,
0x37, 0x15, 0xff, 0x56, 0x99, 0xdd, 0xfa, 0xbe, 0x44, 0x8c, 0xdf, 0x80, 0x71, 0xc7, 0xcb, 0x22,
0x5f, 0x95, 0x3c, 0xae, 0x72, 0x53, 0x95, 0x2d, 0x68, 0x4a, 0x2c, 0xc7, 0xaf, 0x61, 0xc0, 0x57,
0x65, 0xbc, 0x4a, 0x32, 0x6e, 0xf6, 0x64, 0x57, 0xe3, 0xab, 0xd2, 0x4f, 0x32, 0x8e, 0xf7, 0x40,
0x93, 0x8e, 0x59, 0xce, 0xcd, 0xbe, 0xec, 0xf4, 0x45, 0xea, 0xce, 0xf1, 0x31, 0x0c, 0x1f, 0x5d,
0x14, 0xcb, 0x0d, 0x69, 0x72, 0x43, 0x6f, 0x37, 0x1b, 0x6a, 0x9d, 0x64, 0xf2, 0xf8, 0x95, 0x2b,
0x32, 0xb2, 0x4d, 0x22, 0x58, 0xd2, 0x7c, 0x55, 0xf1, 0x55, 0x55, 0xb3, 0x0c, 0xfe, 0xc5, 0xe2,
0xd4, 0xc8, 0x9a, 0x25, 0xdd, 0x24, 0xf8, 0x10, 0xb4, 0xb2, 0xb6, 0xad, 0xa9, 0x8f, 0x94, 0xb1,
0x71, 0x60, 0x6e, 0x08, 0x9e, 0xfa, 0xf9, 0x64, 0x8b, 0x36, 0x50, 0x3c, 0x81, 0xde, 0x52, 0x38,
0xca, 0x04, 0x39, 0xb3, 0xfb, 0x77, 0x23, 0x9e, 0x6c, 0xd1, 0x1a, 0x26, 0xf0, 0x89, 0xb8, 0x55,
0xd3, 0x78, 0x8e, 0x6f, 0x9b, 0x41, 0xe0, 0x25, 0xcc, 0xfa, 0x0e, 0x46, 0xeb, 0xdc, 0xd8, 0x84,
0x9d, 0xc6, 0x07, 0x1e, 0x89, 0xa2, 0x96, 0x6b, 0xb7, 0x01, 0x02, 0x9f, 0xc4, 0x2c, 0x88, 0x03,
0x9f, 0x20, 0x05, 0x23, 0x18, 0x86, 0xb3, 0xa3, 0x73, 0xd7, 0x89, 0xa7, 0x34, 0x98, 0x85, 0xa8,
0x83, 0x5f, 0xc2, 0x8b, 0x90, 0xba, 0x9f, 0x6d, 0x46, 0x1e, 0x4b, 0x5d, 0x3c, 0x82, 0xfd, 0xe8,
0x32, 0x62, 0xc4, 0x5b, 0xb3, 0x3d, 0x45, 0xa8, 0xd6, 0x6f, 0x05, 0x8c, 0xd6, 0xca, 0xda, 0x02,
0x9c, 0xc0, 0x67, 0xc4, 0x67, 0x2d, 0x01, 0x8c, 0x7c, 0x61, 0x71, 0x78, 0x6e, 0xbb, 0x3e, 0x52,
0xb0, 0x01, 0x5a, 0xc4, 0x5c, 0xe7, 0x8c, 0x50, 0xd4, 0xc1, 0x00, 0xfd, 0x88, 0xd9, 0x6c, 0x16,
0xa1, 0x2e, 0xd6, 0xa1, 0x47, 0xbc, 0xe0, 0xd4, 0x45, 0x2a, 0xde, 0x83, 0x57, 0x8c, 0xda, 0x7e,
0x64, 0x3b, 0xcc, 0x0d, 0x04, 0xa3, 0xe7, 0xd9, 0xfe, 0x31, 0xea, 0xe1, 0x31, 0xbc, 0x7b, 0x26,
0xac, 0xf9, 0xdb, 0x53, 0x81, 0x7d, 0xc1, 0x26, 0x5f, 0x2f, 0xd2, 0x44, 0x28, 0x1f, 0x07, 0x1a,
0x1c, 0xe9, 0xeb, 0xc7, 0x78, 0xd5, 0x97, 0x1b, 0x7e, 0xff, 0x27, 0x00, 0x00, 0xff, 0xff, 0x68,
0xd3, 0x79, 0xbb, 0xb5, 0x04, 0x00, 0x00,
}

View File

@ -19,6 +19,16 @@ message ImageMessage {
}
}
message AudioMessage {
bytes payload = 1;
AudioType type = 2;
enum AudioType {
UNKNOWN_AUDIO_TYPE = 0;
AAC = 1;
AMR = 2;
}
}
message ChatMessage {
// Lamport timestamp of the chat message
uint64 clock = 1;
@ -45,6 +55,7 @@ message ChatMessage {
oneof payload {
StickerMessage sticker = 9;
ImageMessage image = 10;
AudioMessage audio = 11;
}
enum MessageType {
@ -64,5 +75,6 @@ message ChatMessage {
// Only local
SYSTEM_MESSAGE_CONTENT_PRIVATE_GROUP = 6;
IMAGE = 7;
AUDIO = 8;
}
}