feat: add `DiscordMessageAttachment` types and APIs

This adds a new `DiscordMessageAttachment` type which is part of
`DiscordMessage`. Along with that type, there's also a new database
table for `discord_message_attachments` and corresponding persistence
APIs.

This commit also changes how chat messages are retrieved.
Here's why:

`DiscordMessage` can have multiple `DiscordMessageAttachment`.
A chat message can have a `DiscordMessage`.

Because we're `LEFT JOIN`'ing the discord message attachments into the
chat messages, there's a possibility of multiple rows per message.

Hence, this commit ensures we collect queried discord message
attachments on chat messages.
This commit is contained in:
Pascal Precht 2022-09-20 11:13:44 +02:00 committed by r4bbit.eth
parent c01ad05525
commit b01a861e8e
9 changed files with 581 additions and 218 deletions

View File

@ -820,7 +820,7 @@ func _1664392661_add_third_party_id_to_waku_messagesUpSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1664392661_add_third_party_id_to_waku_messages.up.sql", size: 70, mode: os.FileMode(0664), modTime: time.Unix(1664392691, 0)}
info := bindataFileInfo{name: "1664392661_add_third_party_id_to_waku_messages.up.sql", size: 70, mode: os.FileMode(0664), modTime: time.Unix(1664438057, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xfd, 0x67, 0x66, 0x9e, 0x66, 0x74, 0xce, 0x1c, 0xb, 0x1b, 0x9d, 0xd5, 0xfc, 0x65, 0xe, 0x83, 0x90, 0x4c, 0x61, 0x4e, 0x6b, 0xe7, 0x86, 0xbe, 0x36, 0x4f, 0x91, 0x36, 0x4, 0x47, 0x7b, 0x82}}
return a, nil
}

View File

@ -91,6 +91,12 @@ const (
OutgoingStatusDelivered = "delivered"
)
type Messages []*Message
func (m Messages) GetClock(i int) uint64 {
return m[i].Clock
}
// Message represents a message record in the database,
// more specifically in user_messages table.
type Message struct {

View File

@ -8,6 +8,12 @@ import (
"github.com/status-im/status-go/protocol/protobuf"
)
type PinnedMessages []*PinnedMessage
func (m PinnedMessages) GetClock(i int) uint64 {
return m[i].Message.Clock
}
type PinMessage struct {
protobuf.PinMessage

View File

@ -5,6 +5,7 @@ import (
"database/sql"
"encoding/json"
"fmt"
"sort"
"strings"
"github.com/status-im/status-go/protocol/common"
@ -22,6 +23,8 @@ LEFT JOIN discord_messages dm
ON m1.discord_message_id = dm.id
LEFT JOIN discord_message_authors dm_author
ON dm.author_id = dm_author.id
LEFT JOIN discord_message_attachments dm_attachment
ON dm.id = dm_attachment.discord_message_id
`
var cursor = "substr('0000000000000000000000000000000000000000000000000000000000000000' || m1.clock_value, -64, 64) || m1.id"
@ -143,6 +146,11 @@ func (db sqlitePersistence) tableUserMessagesAllFieldsJoin() string {
COALESCE(dm_author.discriminator, ""),
COALESCE(dm_author.nickname, ""),
COALESCE(dm_author.avatar_url, ""),
COALESCE(dm_attachment.id, ""),
COALESCE(dm_attachment.discord_message_id, ""),
COALESCE(dm_attachment.url, ""),
COALESCE(dm_attachment.file_name, ""),
COALESCE(dm_attachment.content_type, ""),
m2.source,
m2.text,
m2.parsed_text,
@ -187,10 +195,13 @@ func (db sqlitePersistence) tableUserMessagesScanAllFields(row scanner, message
audio := &protobuf.AudioMessage{}
image := &protobuf.ImageMessage{}
discordMessage := &protobuf.DiscordMessage{
Author: &protobuf.DiscordMessageAuthor{},
Reference: &protobuf.DiscordMessageReference{},
Author: &protobuf.DiscordMessageAuthor{},
Reference: &protobuf.DiscordMessageReference{},
Attachments: []*protobuf.DiscordMessageAttachment{},
}
attachment := &protobuf.DiscordMessageAttachment{}
args := []interface{}{
&message.ID,
&message.WhisperTimestamp,
@ -245,6 +256,11 @@ func (db sqlitePersistence) tableUserMessagesScanAllFields(row scanner, message
&discordMessage.Author.Discriminator,
&discordMessage.Author.Nickname,
&discordMessage.Author.AvatarUrl,
&attachment.Id,
&attachment.MessageId,
&attachment.Url,
&attachment.FileName,
&attachment.ContentType,
&quotedFrom,
&quotedText,
&quotedParsedText,
@ -313,6 +329,10 @@ func (db sqlitePersistence) tableUserMessagesScanAllFields(row scanner, message
}
}
if attachment.Id != "" {
discordMessage.Attachments = append(discordMessage.Attachments, attachment)
}
switch message.ContentType {
case protobuf.ChatMessage_STICKER:
message.Payload = &protobuf.ChatMessage_Sticker{Sticker: sticker}
@ -365,8 +385,9 @@ func (db sqlitePersistence) tableUserMessagesAllValues(message *common.Message)
discordMessage := message.GetDiscordMessage()
if discordMessage == nil {
discordMessage = &protobuf.DiscordMessage{
Author: &protobuf.DiscordMessageAuthor{},
Reference: &protobuf.DiscordMessageReference{},
Author: &protobuf.DiscordMessageAuthor{},
Reference: &protobuf.DiscordMessageReference{},
Attachments: make([]*protobuf.DiscordMessageAttachment, 0),
}
}
@ -459,25 +480,17 @@ func (db sqlitePersistence) messageByID(tx *sql.Tx, id string) (*common.Message,
}()
}
var message common.Message
query := db.buildMessagesQuery("WHERE m1.id = ?")
row := tx.QueryRow(query, id)
err = db.tableUserMessagesScanAllFields(row, &message)
switch err {
case sql.ErrNoRows:
return nil, common.ErrRecordNotFound
case nil:
return &message, nil
default:
rows, err := tx.Query(query, id)
if err != nil {
return nil, err
}
defer rows.Close()
return getMessageFromScanRows(db, rows)
}
func (db sqlitePersistence) MessageByCommandID(chatID, id string) (*common.Message, error) {
var message common.Message
where := `WHERE
m1.command_id = ?
AND
@ -485,16 +498,12 @@ func (db sqlitePersistence) MessageByCommandID(chatID, id string) (*common.Messa
ORDER BY m1.clock_value DESC
LIMIT 1`
query := db.buildMessagesQuery(where)
row := db.db.QueryRow(query, id, chatID)
err := db.tableUserMessagesScanAllFields(row, &message)
switch err {
case sql.ErrNoRows:
return nil, common.ErrRecordNotFound
case nil:
return &message, nil
default:
rows, err := db.db.Query(query, id, chatID)
if err != nil {
return nil, err
}
defer rows.Close()
return getMessageFromScanRows(db, rows)
}
func (db sqlitePersistence) MessageByID(id string) (*common.Message, error) {
@ -552,17 +561,7 @@ func (db sqlitePersistence) MessagesByIDs(ids []string) ([]*common.Message, erro
return nil, err
}
defer rows.Close()
var result []*common.Message
for rows.Next() {
var message common.Message
if err := db.tableUserMessagesScanAllFields(rows, &message); err != nil {
return nil, err
}
result = append(result, &message)
}
return result, nil
return getMessagesFromScanRows(db, rows)
}
// MessageByChatID returns all messages for a given chatID in descending order.
@ -597,20 +596,9 @@ func (db sqlitePersistence) MessageByChatID(chatID string, currCursor string, li
}
defer rows.Close()
var (
result []*common.Message
cursors []string
)
for rows.Next() {
var (
message common.Message
cursor string
)
if err := db.tableUserMessagesScanAllFields(rows, &message, &cursor); err != nil {
return nil, "", err
}
result = append(result, &message)
cursors = append(cursors, cursor)
result, cursors, err := getMessagesAndCursorsFromScanRows(db, rows)
if err != nil {
return nil, "", err
}
var newCursor string
@ -670,20 +658,9 @@ func (db sqlitePersistence) PendingContactRequests(currCursor string, limit int)
}
defer rows.Close()
var (
result []*common.Message
cursors []string
)
for rows.Next() {
var (
message common.Message
cursor string
)
if err := db.tableUserMessagesScanAllFields(rows, &message, &cursor); err != nil {
return nil, "", err
}
result = append(result, &message)
cursors = append(cursors, cursor)
result, cursors, err := getMessagesAndCursorsFromScanRows(db, rows)
if err != nil {
return nil, "", err
}
var newCursor string
@ -785,22 +762,7 @@ func (db sqlitePersistence) AllMessageByChatIDWhichMatchTerm(chatID string, sear
return nil, err
}
defer rows.Close()
var (
result []*common.Message
)
for rows.Next() {
var (
message common.Message
cursor string
)
if err := db.tableUserMessagesScanAllFields(rows, &message, &cursor); err != nil {
return nil, err
}
result = append(result, &message)
}
return result, nil
return getMessagesFromScanRows(db, rows)
}
// AllMessagesFromChatsAndCommunitiesWhichMatchTerm returns all messages which match the search
@ -862,29 +824,13 @@ func (db sqlitePersistence) AllMessagesFromChatsAndCommunitiesWhichMatchTerm(com
ORDER BY cursor DESC`, finalCond)
finalQuery := db.buildMessagesQueryWithAdditionalFields(cursorField, where)
rows, err := db.db.Query(finalQuery, idsArgs...)
if err != nil {
return nil, err
}
defer rows.Close()
var (
result []*common.Message
)
for rows.Next() {
var (
message common.Message
cursor string
)
if err := db.tableUserMessagesScanAllFields(rows, &message, &cursor); err != nil {
return nil, err
}
result = append(result, &message)
}
return result, nil
return getMessagesFromScanRows(db, rows)
}
func (db sqlitePersistence) AllChatIDsByCommunity(communityID string) ([]string, error) {
@ -965,6 +911,11 @@ func (db sqlitePersistence) PinnedMessageByChatIDs(chatIDs []string, currCursor
ON
dm.author_id = dm_author.id
LEFT JOIN
discord_message_attachments dm_attachment
ON
dm.id = dm_attachment.discord_message_id
WHERE
pm.pinned = 1
AND NOT(m1.hide) AND m1.local_chat_id IN %s %s
@ -978,27 +929,9 @@ func (db sqlitePersistence) PinnedMessageByChatIDs(chatIDs []string, currCursor
}
defer rows.Close()
var (
result []*common.PinnedMessage
cursors []string
)
for rows.Next() {
var (
message common.Message
pinnedAt uint64
pinnedBy string
cursor string
)
if err := db.tableUserMessagesScanAllFields(rows, &message, &pinnedAt, &pinnedBy, &cursor); err != nil {
return nil, "", err
}
pinnedMessage := &common.PinnedMessage{
Message: &message,
PinnedAt: pinnedAt,
PinnedBy: pinnedBy,
}
result = append(result, pinnedMessage)
cursors = append(cursors, cursor)
result, cursors, err := getPinnedMessagesAndCursorsFromScanRows(db, rows)
if err != nil {
return nil, "", err
}
var newCursor string
@ -1048,20 +981,9 @@ func (db sqlitePersistence) MessageByChatIDs(chatIDs []string, currCursor string
}
defer rows.Close()
var (
result []*common.Message
cursors []string
)
for rows.Next() {
var (
message common.Message
cursor string
)
if err := db.tableUserMessagesScanAllFields(rows, &message, &cursor); err != nil {
return nil, "", err
}
result = append(result, &message)
cursors = append(cursors, cursor)
result, cursors, err := getMessagesAndCursorsFromScanRows(db, rows)
if err != nil {
return nil, "", err
}
var newCursor string
@ -1884,6 +1806,52 @@ func (db sqlitePersistence) SaveDiscordMessages(messages []*protobuf.DiscordMess
return
}
func (db sqlitePersistence) HasDiscordMessageAttachmentPayload(id string) (hasPayload bool, err error) {
err = db.db.QueryRow(`SELECT EXISTS(SELECT 1 FROM discord_message_attachments WHERE id = ? AND payload NOT NULL)`, id).Scan(&hasPayload)
return hasPayload, err
}
func (db sqlitePersistence) SaveDiscordMessageAttachments(attachments []*protobuf.DiscordMessageAttachment) (err error) {
tx, err := db.db.BeginTx(context.Background(), &sql.TxOptions{})
if err != nil {
return
}
defer func() {
if err == nil {
err = tx.Commit()
return
}
// don't shadow original error
_ = tx.Rollback()
}()
query := "INSERT OR REPLACE INTO discord_message_attachments(id,discord_message_id,url,file_name,file_size_bytes,payload, content_type) VALUES (?,?,?,?,?,?,?)"
stmt, err := tx.Prepare(query)
if err != nil {
return
}
defer stmt.Close()
if err != nil {
return
}
for _, attachment := range attachments {
_, err = stmt.Exec(
attachment.GetId(),
attachment.GetMessageId(),
attachment.GetUrl(),
attachment.GetFileName(),
attachment.GetFileSizeBytes(),
attachment.GetPayload(),
attachment.GetContentType(),
)
if err != nil {
return
}
}
return
}
func (db sqlitePersistence) SaveEmojiReaction(emojiReaction *EmojiReaction) (err error) {
query := "INSERT INTO emoji_reactions(id,clock_value,source,emoji_id,message_id,chat_id,local_chat_id,retracted) VALUES (?,?,?,?,?,?,?,?)"
stmt, err := db.db.Prepare(query)
@ -2241,3 +2209,155 @@ func (db sqlitePersistence) SetContactRequestState(id string, state common.Conta
_, err := db.db.Exec(`UPDATE user_messages SET contact_request_state = ? WHERE id = ?`, state, id)
return err
}
func getUpdatedChatMessagePayload(originalMessage *protobuf.DiscordMessage, attachmentMessage *protobuf.DiscordMessage) *protobuf.ChatMessage_DiscordMessage {
originalMessage.Attachments = append(originalMessage.Attachments, attachmentMessage.Attachments...)
return &protobuf.ChatMessage_DiscordMessage{
DiscordMessage: originalMessage,
}
}
func getMessageFromScanRows(db sqlitePersistence, rows *sql.Rows) (*common.Message, error) {
var msg *common.Message
for rows.Next() {
// There's a possibility of multiple rows per message if the
// message has a discordMessage and the discordMessage has multiple
// attachments
//
// Hence, we make sure we're aggregating all attachments on a single
// common.Message
var message common.Message
err := db.tableUserMessagesScanAllFields(rows, &message)
if err != nil {
return nil, err
}
if msg == nil {
msg = &message
} else if discordMessage := msg.GetDiscordMessage(); discordMessage != nil {
msg.Payload = getUpdatedChatMessagePayload(discordMessage, message.GetDiscordMessage())
}
}
if msg == nil {
return nil, common.ErrRecordNotFound
}
return msg, nil
}
type HasClocks interface {
GetClock(i int) uint64
}
func SortByClock(msgs HasClocks) {
sort.Slice(msgs, func(i, j int) bool {
return msgs.GetClock(j) < msgs.GetClock(i)
})
}
func getMessagesFromScanRows(db sqlitePersistence, rows *sql.Rows) ([]*common.Message, error) {
messageIdx := make(map[string]*common.Message, 0)
for rows.Next() {
// There's a possibility of multiple rows per message if the
// message has a discordMessage and the discordMessage has multiple
// attachments
//
// Hence, we make sure we're aggregating all attachments on a single
// common.Message
var message common.Message
if err := db.tableUserMessagesScanAllFields(rows, &message); err != nil {
return nil, err
}
if msg, ok := messageIdx[message.ID]; !ok {
messageIdx[message.ID] = &message
} else if discordMessage := msg.GetDiscordMessage(); discordMessage != nil {
msg.Payload = getUpdatedChatMessagePayload(discordMessage, message.GetDiscordMessage())
messageIdx[message.ID] = msg
}
}
var messages common.Messages
for _, message := range messageIdx {
messages = append(messages, message)
}
SortByClock(messages)
return messages, nil
}
func getMessagesAndCursorsFromScanRows(db sqlitePersistence, rows *sql.Rows) ([]*common.Message, []string, error) {
var cursors []string
messageIdx := make(map[string]*common.Message, 0)
for rows.Next() {
// There's a possibility of multiple rows per message if the
// message has a discordMessage and the discordMessage has multiple
// attachments
//
// Hence, we make sure we're aggregating all attachments on a single
// common.Message
var (
message common.Message
cursor string
)
if err := db.tableUserMessagesScanAllFields(rows, &message, &cursor); err != nil {
return nil, nil, err
}
if msg, ok := messageIdx[message.ID]; !ok {
messageIdx[message.ID] = &message
cursors = append(cursors, cursor)
} else if discordMessage := msg.GetDiscordMessage(); discordMessage != nil {
msg.Payload = getUpdatedChatMessagePayload(discordMessage, message.GetDiscordMessage())
messageIdx[message.ID] = msg
}
}
var messages common.Messages
for _, message := range messageIdx {
messages = append(messages, message)
}
SortByClock(messages)
return messages, cursors, nil
}
func getPinnedMessagesAndCursorsFromScanRows(db sqlitePersistence, rows *sql.Rows) ([]*common.PinnedMessage, []string, error) {
var cursors []string
messageIdx := make(map[string]*common.PinnedMessage, 0)
for rows.Next() {
var (
message common.Message
pinnedAt uint64
pinnedBy string
cursor string
)
if err := db.tableUserMessagesScanAllFields(rows, &message, &pinnedAt, &pinnedBy, &cursor); err != nil {
return nil, nil, err
}
if msg, ok := messageIdx[message.ID]; !ok {
pinnedMessage := &common.PinnedMessage{
Message: &message,
PinnedAt: pinnedAt,
PinnedBy: pinnedBy,
}
messageIdx[message.ID] = pinnedMessage
cursors = append(cursors, cursor)
} else if discordMessage := msg.Message.GetDiscordMessage(); discordMessage != nil {
msg.Message.Payload = getUpdatedChatMessagePayload(discordMessage, message.GetDiscordMessage())
messageIdx[message.ID] = msg
}
}
var messages common.PinnedMessages
for _, message := range messageIdx {
messages = append(messages, message)
}
SortByClock(messages)
return messages, cursors, nil
}

View File

@ -62,6 +62,7 @@
// 1662106895_add_chat_first_message_timestamp.up.sql (113B)
// 1662723928_add_discord_author_image_fields.up.sql (75B)
// 1664195977_add_deleted_for_mes.up.sql (352B)
// 1664367420_add_discord_attachments_table.up.sql (350B)
// README.md (554B)
// doc.go (850B)
@ -1372,6 +1373,26 @@ func _1664195977_add_deleted_for_mesUpSql() (*asset, error) {
return a, nil
}
var __1664367420_add_discord_attachments_tableUpSql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x7c\x90\xc1\x4e\xc3\x30\x0c\x86\xef\x79\x8a\xff\xc8\x24\xde\x60\xa7\x96\x05\x11\x51\x12\xd4\x79\x5a\x77\x8a\x42\x63\x20\x52\x93\x4e\x4b\x90\x28\x4f\x8f\x80\x55\x1a\xaa\xe0\x68\xff\xfe\x2c\x7f\xbe\x69\x65\x45\x12\x54\xd5\x8d\x84\xba\x85\x36\x04\xd9\xa9\x2d\x6d\xe1\x43\xee\xc7\x93\xb7\x91\x73\x76\x2f\x6c\x5d\x29\xae\x7f\x8d\x9c\x4a\xc6\x95\x00\x82\x07\xc9\x8e\xf0\xd8\xaa\x87\xaa\x3d\xe0\x5e\x1e\xbe\x71\xbd\x6b\x9a\x6b\x81\x05\x3f\xcf\xcf\xf9\xdb\x69\x38\x37\x2e\xa0\xe7\x30\xb0\x4d\x2e\xf2\x1f\x51\x0e\x1f\x6c\x9f\xa6\xc2\x19\x4a\xff\xce\x8f\x6e\x1a\x46\xe7\x51\x37\xa6\xfe\xaa\xfb\x31\x15\x4e\xc5\x96\xe9\xf8\xb3\x4d\xac\xb0\x57\x74\x67\x76\x84\xd6\xec\xd5\x66\x2d\xc4\x59\x5f\xe9\x8d\xec\xe0\xe3\xa5\xe3\x7c\x77\xb6\xc1\xbf\xc3\xe8\xff\xff\xb1\x94\x5d\xad\x85\xf8\x0c\x00\x00\xff\xff\xec\xb0\x4b\x53\x5e\x01\x00\x00")
func _1664367420_add_discord_attachments_tableUpSqlBytes() ([]byte, error) {
return bindataRead(
__1664367420_add_discord_attachments_tableUpSql,
"1664367420_add_discord_attachments_table.up.sql",
)
}
func _1664367420_add_discord_attachments_tableUpSql() (*asset, error) {
bytes, err := _1664367420_add_discord_attachments_tableUpSqlBytes()
if err != nil {
return nil, err
}
info := bindataFileInfo{name: "1664367420_add_discord_attachments_table.up.sql", size: 350, mode: os.FileMode(0664), modTime: time.Unix(1664438057, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x48, 0xe1, 0xb6, 0x4f, 0x6f, 0x92, 0x0, 0xb4, 0xf, 0x55, 0x12, 0x1c, 0x98, 0x6d, 0xbc, 0x1e, 0xfd, 0xae, 0x1c, 0xce, 0xd1, 0x3d, 0x2, 0x21, 0x2e, 0xc0, 0x13, 0xa, 0xb2, 0xec, 0x81, 0x13}}
return a, nil
}
var _readmeMd = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x54\x91\xc1\xce\xd3\x30\x10\x84\xef\x7e\x8a\x91\x7a\x01\xa9\x2a\x8f\xc0\x0d\x71\x82\x03\x48\x1c\xc9\x36\x9e\x36\x96\x1c\x6f\xf0\xae\x93\xe6\xed\x91\xa3\xc2\xdf\xff\x66\xed\xd8\x33\xdf\x78\x4f\xa7\x13\xbe\xea\x06\x57\x6c\x35\x39\x31\xa7\x7b\x15\x4f\x5a\xec\x73\x08\xbf\x08\x2d\x79\x7f\x4a\x43\x5b\x86\x17\xfd\x8c\x21\xea\x56\x5e\x47\x90\x4a\x14\x75\x48\xde\x64\x37\x2c\x6a\x96\xae\x99\x48\x05\xf6\x27\x77\x13\xad\x08\xae\x8a\x51\xe7\x25\xf3\xf1\xa9\x9f\xf9\x58\x58\x2c\xad\xbc\xe0\x8b\x56\xf0\x21\x5d\xeb\x4c\x95\xb3\xae\x84\x60\xd4\xdc\xe6\x82\x5d\x1b\x36\x6d\x39\x62\x92\xf5\xb8\x11\xdb\x92\xd3\x28\xce\xe0\x13\xe1\x72\xcd\x3c\x63\xd4\x65\x87\xae\xac\xe8\xc3\x28\x2e\x67\x44\x66\x3a\x21\x25\xa2\x72\xac\x14\x67\xbc\x84\x9f\x53\x32\x8c\x52\x70\x25\x56\xd6\xfd\x8d\x05\x37\xad\x30\x9d\x9f\xa6\x86\x0f\xcd\x58\x7f\xcf\x34\x93\x3b\xed\x90\x9f\xa4\x1f\xcf\x30\x85\x4d\x07\x58\xaf\x7f\x25\xc4\x9d\xf3\x72\x64\x84\xd0\x7f\xf9\x9b\x3a\x2d\x84\xef\x85\x48\x66\x8d\xd8\x88\x9b\x8c\x8c\x98\x5b\xf6\x74\x14\x4e\x33\x0d\xc9\xe0\x93\x38\xda\x12\xc5\x69\xbd\xe4\xf0\x2e\x7a\x78\x07\x1c\xfe\x13\x9f\x91\x29\x31\x95\x7b\x7f\x62\x59\x37\xb4\xe5\x5e\x25\xfe\x33\xee\xd5\x53\x71\xd6\xda\x3a\xd8\xcb\xde\x2e\xf8\xa1\x90\x55\x53\x0c\xc7\xaa\x0d\xe9\x76\x14\x29\x1c\x7b\x68\xdd\x2f\xe1\x6f\x00\x00\x00\xff\xff\x3c\x0a\xc2\xfe\x2a\x02\x00\x00")
func readmeMdBytes() ([]byte, error) {
@ -1627,6 +1648,8 @@ var _bindata = map[string]func() (*asset, error){
"1664195977_add_deleted_for_mes.up.sql": _1664195977_add_deleted_for_mesUpSql,
"1664367420_add_discord_attachments_table.up.sql": _1664367420_add_discord_attachments_tableUpSql,
"README.md": readmeMd,
"doc.go": docGo,
@ -1735,8 +1758,9 @@ var _bintree = &bintree{nil, map[string]*bintree{
"1662106895_add_chat_first_message_timestamp.up.sql": &bintree{_1662106895_add_chat_first_message_timestampUpSql, map[string]*bintree{}},
"1662723928_add_discord_author_image_fields.up.sql": &bintree{_1662723928_add_discord_author_image_fieldsUpSql, map[string]*bintree{}},
"1664195977_add_deleted_for_mes.up.sql": &bintree{_1664195977_add_deleted_for_mesUpSql, map[string]*bintree{}},
"README.md": &bintree{readmeMd, map[string]*bintree{}},
"doc.go": &bintree{docGo, map[string]*bintree{}},
"1664367420_add_discord_attachments_table.up.sql": &bintree{_1664367420_add_discord_attachments_tableUpSql, 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.

View File

@ -0,0 +1,12 @@
CREATE TABLE IF NOT EXISTS discord_message_attachments (
id TEXT PRIMARY KEY NOT NULL,
discord_message_id TEXT NULL,
url TEXT NOT NULL,
file_name TEXT NOT NULL,
file_size_bytes INT NOT NULL,
payload BLOB,
content_type TEXT
) WITHOUT ROWID;
CREATE INDEX dm_attachments_messages_idx ON discord_message_attachments (discord_message_id);

View File

@ -60,6 +60,30 @@ func TestMessagesByIDs(t *testing.T) {
require.Len(t, m, 10)
}
func TestMessagesByIDs_WithDiscordMessagesPayload(t *testing.T) {
db, err := openTestDB()
require.NoError(t, err)
p := newSQLitePersistence(db)
var ids []string
for i := 0; i < 10; i++ {
id := strconv.Itoa(i)
err := insertMinimalMessage(p, id)
require.NoError(t, err)
err = insertMinimalDiscordMessage(p, id, id)
require.NoError(t, err)
ids = append(ids, id)
}
m, err := p.MessagesByIDs(ids)
require.NoError(t, err)
require.Len(t, m, 10)
for _, _m := range m {
require.NotNil(t, _m.GetDiscordMessage())
}
}
func TestMessageByID(t *testing.T) {
db, err := openTestDB()
require.NoError(t, err)
@ -93,6 +117,29 @@ func TestMessageByID_WithDiscordMessagePayload(t *testing.T) {
require.EqualValues(t, "2", m.GetDiscordMessage().Author.Id)
}
func TestMessageByID_WithDiscordMessageAttachmentPayload(t *testing.T) {
db, err := openTestDB()
require.NoError(t, err)
p := newSQLitePersistence(db)
id := "1"
discordMessageID := "2"
err = insertDiscordMessageWithAttachments(p, id, discordMessageID)
require.NoError(t, err)
m, err := p.MessageByID(id)
require.NoError(t, err)
require.EqualValues(t, id, m.ID)
dm := m.GetDiscordMessage()
require.NotNil(t, dm)
require.EqualValues(t, discordMessageID, dm.Id)
require.NotNil(t, dm.Attachments)
require.Len(t, dm.Attachments, 2)
}
func TestMessagesExist(t *testing.T) {
db, err := openTestDB()
require.NoError(t, err)
@ -734,6 +781,32 @@ func insertMinimalMessage(p *sqlitePersistence, id string) error {
}})
}
func insertDiscordMessageWithAttachments(p *sqlitePersistence, id string, discordMessageID string) error {
err := insertMinimalDiscordMessage(p, id, discordMessageID)
if err != nil {
return err
}
attachment := &protobuf.DiscordMessageAttachment{
Id: "1",
MessageId: discordMessageID,
Url: "https://does-not-exist.com",
Payload: []byte{1, 2, 3, 4},
}
attachment2 := &protobuf.DiscordMessageAttachment{
Id: "2",
MessageId: discordMessageID,
Url: "https://does-not-exist.com",
Payload: []byte{5, 6, 7, 8},
}
return p.SaveDiscordMessageAttachments([]*protobuf.DiscordMessageAttachment{
attachment,
attachment2,
})
}
func insertMinimalDiscordMessage(p *sqlitePersistence, id string, discordMessageID string) error {
discordMessage := &protobuf.DiscordMessage{
Id: discordMessageID,

View File

@ -105,7 +105,7 @@ func (x ChatMessage_ContentType) String() string {
}
func (ChatMessage_ContentType) EnumDescriptor() ([]byte, []int) {
return fileDescriptor_263952f55fd35689, []int{9, 0}
return fileDescriptor_263952f55fd35689, []int{10, 0}
}
type StickerMessage struct {
@ -460,16 +460,17 @@ func (m *DeleteForMeMessage) GetMessageId() string {
}
type DiscordMessage struct {
Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
Type string `protobuf:"bytes,2,opt,name=type,proto3" json:"type,omitempty"`
Timestamp string `protobuf:"bytes,3,opt,name=timestamp,proto3" json:"timestamp,omitempty"`
TimestampEdited string `protobuf:"bytes,4,opt,name=timestampEdited,proto3" json:"timestampEdited,omitempty"`
Content string `protobuf:"bytes,5,opt,name=content,proto3" json:"content,omitempty"`
Author *DiscordMessageAuthor `protobuf:"bytes,6,opt,name=author,proto3" json:"author,omitempty"`
Reference *DiscordMessageReference `protobuf:"bytes,7,opt,name=reference,proto3" json:"reference,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
Type string `protobuf:"bytes,2,opt,name=type,proto3" json:"type,omitempty"`
Timestamp string `protobuf:"bytes,3,opt,name=timestamp,proto3" json:"timestamp,omitempty"`
TimestampEdited string `protobuf:"bytes,4,opt,name=timestampEdited,proto3" json:"timestampEdited,omitempty"`
Content string `protobuf:"bytes,5,opt,name=content,proto3" json:"content,omitempty"`
Author *DiscordMessageAuthor `protobuf:"bytes,6,opt,name=author,proto3" json:"author,omitempty"`
Reference *DiscordMessageReference `protobuf:"bytes,7,opt,name=reference,proto3" json:"reference,omitempty"`
Attachments []*DiscordMessageAttachment `protobuf:"bytes,8,rep,name=attachments,proto3" json:"attachments,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *DiscordMessage) Reset() { *m = DiscordMessage{} }
@ -546,6 +547,13 @@ func (m *DiscordMessage) GetReference() *DiscordMessageReference {
return nil
}
func (m *DiscordMessage) GetAttachments() []*DiscordMessageAttachment {
if m != nil {
return m.Attachments
}
return nil
}
type DiscordMessageAuthor struct {
Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"`
@ -688,6 +696,101 @@ func (m *DiscordMessageReference) GetGuildId() string {
return ""
}
type DiscordMessageAttachment struct {
Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
MessageId string `protobuf:"bytes,2,opt,name=messageId,proto3" json:"messageId,omitempty"`
Url string `protobuf:"bytes,3,opt,name=url,proto3" json:"url,omitempty"`
FileName string `protobuf:"bytes,4,opt,name=fileName,proto3" json:"fileName,omitempty"`
FileSizeBytes uint64 `protobuf:"varint,5,opt,name=fileSizeBytes,proto3" json:"fileSizeBytes,omitempty"`
ContentType string `protobuf:"bytes,6,opt,name=contentType,proto3" json:"contentType,omitempty"`
Payload []byte `protobuf:"bytes,7,opt,name=payload,proto3" json:"payload,omitempty"`
LocalUrl string `protobuf:"bytes,8,opt,name=localUrl,proto3" json:"localUrl,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *DiscordMessageAttachment) Reset() { *m = DiscordMessageAttachment{} }
func (m *DiscordMessageAttachment) String() string { return proto.CompactTextString(m) }
func (*DiscordMessageAttachment) ProtoMessage() {}
func (*DiscordMessageAttachment) Descriptor() ([]byte, []int) {
return fileDescriptor_263952f55fd35689, []int{9}
}
func (m *DiscordMessageAttachment) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_DiscordMessageAttachment.Unmarshal(m, b)
}
func (m *DiscordMessageAttachment) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_DiscordMessageAttachment.Marshal(b, m, deterministic)
}
func (m *DiscordMessageAttachment) XXX_Merge(src proto.Message) {
xxx_messageInfo_DiscordMessageAttachment.Merge(m, src)
}
func (m *DiscordMessageAttachment) XXX_Size() int {
return xxx_messageInfo_DiscordMessageAttachment.Size(m)
}
func (m *DiscordMessageAttachment) XXX_DiscardUnknown() {
xxx_messageInfo_DiscordMessageAttachment.DiscardUnknown(m)
}
var xxx_messageInfo_DiscordMessageAttachment proto.InternalMessageInfo
func (m *DiscordMessageAttachment) GetId() string {
if m != nil {
return m.Id
}
return ""
}
func (m *DiscordMessageAttachment) GetMessageId() string {
if m != nil {
return m.MessageId
}
return ""
}
func (m *DiscordMessageAttachment) GetUrl() string {
if m != nil {
return m.Url
}
return ""
}
func (m *DiscordMessageAttachment) GetFileName() string {
if m != nil {
return m.FileName
}
return ""
}
func (m *DiscordMessageAttachment) GetFileSizeBytes() uint64 {
if m != nil {
return m.FileSizeBytes
}
return 0
}
func (m *DiscordMessageAttachment) GetContentType() string {
if m != nil {
return m.ContentType
}
return ""
}
func (m *DiscordMessageAttachment) GetPayload() []byte {
if m != nil {
return m.Payload
}
return nil
}
func (m *DiscordMessageAttachment) GetLocalUrl() string {
if m != nil {
return m.LocalUrl
}
return ""
}
type ChatMessage struct {
// Lamport timestamp of the chat message
Clock uint64 `protobuf:"varint,1,opt,name=clock,proto3" json:"clock,omitempty"`
@ -733,7 +836,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{9}
return fileDescriptor_263952f55fd35689, []int{10}
}
func (m *ChatMessage) XXX_Unmarshal(b []byte) error {
@ -944,7 +1047,7 @@ func (m *ContactRequestSignature) Reset() { *m = ContactRequestSignature
func (m *ContactRequestSignature) String() string { return proto.CompactTextString(m) }
func (*ContactRequestSignature) ProtoMessage() {}
func (*ContactRequestSignature) Descriptor() ([]byte, []int) {
return fileDescriptor_263952f55fd35689, []int{10}
return fileDescriptor_263952f55fd35689, []int{11}
}
func (m *ContactRequestSignature) XXX_Unmarshal(b []byte) error {
@ -991,6 +1094,7 @@ func init() {
proto.RegisterType((*DiscordMessage)(nil), "protobuf.DiscordMessage")
proto.RegisterType((*DiscordMessageAuthor)(nil), "protobuf.DiscordMessageAuthor")
proto.RegisterType((*DiscordMessageReference)(nil), "protobuf.DiscordMessageReference")
proto.RegisterType((*DiscordMessageAttachment)(nil), "protobuf.DiscordMessageAttachment")
proto.RegisterType((*ChatMessage)(nil), "protobuf.ChatMessage")
proto.RegisterType((*ContactRequestSignature)(nil), "protobuf.ContactRequestSignature")
}
@ -1000,75 +1104,81 @@ func init() {
}
var fileDescriptor_263952f55fd35689 = []byte{
// 1119 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x56, 0xcd, 0x6e, 0xdb, 0x46,
0x10, 0xb6, 0x64, 0xfd, 0x71, 0x28, 0xcb, 0xec, 0x26, 0x8d, 0xd9, 0x34, 0xb5, 0x1d, 0x22, 0x40,
0x74, 0x52, 0x81, 0x34, 0x2d, 0x02, 0xf4, 0x50, 0x30, 0x12, 0x6b, 0xb3, 0xa9, 0x7e, 0xb2, 0xa4,
0xda, 0xba, 0x17, 0x62, 0x43, 0xae, 0x2d, 0xc2, 0x22, 0xa9, 0x92, 0x54, 0x50, 0x3f, 0x40, 0xcf,
0x7d, 0x92, 0x5e, 0x7b, 0xed, 0xdb, 0xf4, 0x01, 0xfa, 0x04, 0xc5, 0x2e, 0xb9, 0x24, 0x25, 0x58,
0xb6, 0x4f, 0xda, 0x99, 0x9d, 0x99, 0xfd, 0xf4, 0xcd, 0xb7, 0xb3, 0x04, 0xe4, 0x2e, 0x48, 0xea,
0x04, 0x34, 0x49, 0xc8, 0x15, 0x1d, 0xac, 0xe2, 0x28, 0x8d, 0x50, 0x87, 0xff, 0x7c, 0x58, 0x5f,
0x3e, 0x95, 0x69, 0xb8, 0x0e, 0x92, 0xcc, 0xad, 0xbd, 0x81, 0x9e, 0x95, 0xfa, 0xee, 0x35, 0x8d,
0xc7, 0x59, 0x38, 0x42, 0xd0, 0x58, 0x90, 0x64, 0xa1, 0xd6, 0x4e, 0x6b, 0x7d, 0x09, 0xf3, 0x35,
0xf3, 0xad, 0x88, 0x7b, 0xad, 0xd6, 0x4f, 0x6b, 0xfd, 0x26, 0xe6, 0x6b, 0xed, 0x3d, 0x74, 0xcd,
0x80, 0x5c, 0x51, 0x91, 0xa7, 0x42, 0x7b, 0x45, 0x6e, 0x96, 0x11, 0xf1, 0x78, 0x6a, 0x17, 0x0b,
0x13, 0xbd, 0x84, 0x46, 0x7a, 0xb3, 0xa2, 0x3c, 0xbb, 0xf7, 0xea, 0xd1, 0x40, 0x20, 0x19, 0xf0,
0x7c, 0xfb, 0x66, 0x45, 0x31, 0x0f, 0xd0, 0xfe, 0xae, 0x41, 0x57, 0x5f, 0x7b, 0x7e, 0x74, 0x7f,
0xcd, 0xd7, 0x1b, 0x35, 0x4f, 0xcb, 0x9a, 0xd5, 0xfc, 0xcc, 0x28, 0x0f, 0x40, 0x27, 0x20, 0x7b,
0xeb, 0x98, 0xa4, 0x7e, 0x14, 0x3a, 0x41, 0xa2, 0xee, 0x9f, 0xd6, 0xfa, 0x0d, 0x0c, 0xc2, 0x35,
0x4e, 0xb4, 0xaf, 0x41, 0x2a, 0x72, 0xd0, 0x13, 0x40, 0xf3, 0xc9, 0xbb, 0xc9, 0xf4, 0xe7, 0x89,
0xa3, 0xcf, 0x47, 0xe6, 0xd4, 0xb1, 0x2f, 0x66, 0x86, 0xb2, 0x87, 0xda, 0xb0, 0xaf, 0xeb, 0x43,
0xa5, 0xc6, 0x17, 0x63, 0xac, 0xd4, 0xb5, 0x7f, 0x6a, 0x20, 0x1b, 0x9e, 0x9f, 0x0a, 0xdc, 0x8f,
0xa1, 0xe9, 0x2e, 0x23, 0xf7, 0x9a, 0xa3, 0x6e, 0xe0, 0xcc, 0x60, 0x2c, 0xa6, 0xf4, 0xf7, 0x94,
0x63, 0x96, 0x30, 0x5f, 0xa3, 0x23, 0x68, 0xf3, 0x66, 0xf9, 0x1e, 0x47, 0x23, 0xe1, 0x16, 0x33,
0x4d, 0x0f, 0x7d, 0x01, 0x90, 0x37, 0x90, 0xed, 0x35, 0xf8, 0x9e, 0x94, 0x7b, 0x4c, 0x8f, 0x9d,
0x70, 0x15, 0x93, 0x30, 0x55, 0x9b, 0x9c, 0x97, 0xcc, 0x40, 0x6f, 0xa0, 0x2b, 0x92, 0x38, 0x3b,
0x2d, 0xce, 0xce, 0xa7, 0x25, 0x3b, 0x39, 0x40, 0x4e, 0x89, 0x1c, 0x94, 0x86, 0xf6, 0x57, 0x0d,
0x0e, 0x46, 0x74, 0x49, 0x53, 0x7a, 0xf7, 0x7f, 0xa8, 0xe0, 0xad, 0xdf, 0x81, 0x77, 0x7f, 0x27,
0xde, 0xc6, 0x5d, 0x78, 0x9b, 0x0f, 0xc6, 0x6b, 0x02, 0xca, 0xe0, 0x7e, 0x1f, 0xc5, 0xe3, 0x7b,
0x30, 0x6f, 0x42, 0xab, 0x6f, 0x41, 0xd3, 0xfe, 0xac, 0x43, 0x6f, 0xe4, 0x27, 0x6e, 0x14, 0x7b,
0xa2, 0x4e, 0x0f, 0xea, 0xbe, 0x97, 0xdf, 0x80, 0xba, 0xef, 0xf1, 0xce, 0x09, 0xb5, 0x49, 0xb9,
0x96, 0x9e, 0x81, 0x94, 0xfa, 0x01, 0x4d, 0x52, 0x12, 0xac, 0xc4, 0xff, 0x2d, 0x1c, 0xa8, 0x0f,
0x87, 0x85, 0xc1, 0x94, 0x41, 0x45, 0x0f, 0xb7, 0xdd, 0x4c, 0xe3, 0x6e, 0x14, 0xa6, 0x34, 0xef,
0xa5, 0x84, 0x85, 0x89, 0xbe, 0x81, 0x16, 0x59, 0xa7, 0x8b, 0x28, 0xe6, 0x7d, 0x94, 0x5f, 0x1d,
0x97, 0xbc, 0x6c, 0xe2, 0xd5, 0x79, 0x14, 0xce, 0xa3, 0xd1, 0x77, 0x20, 0xc5, 0xf4, 0x92, 0xc6,
0x34, 0x74, 0xa9, 0xda, 0xe6, 0xa9, 0xcf, 0x77, 0xa5, 0x62, 0x11, 0x88, 0xcb, 0x1c, 0xed, 0xdf,
0x1a, 0x3c, 0xbe, 0xed, 0x84, 0xdb, 0x78, 0x09, 0x49, 0x50, 0xf0, 0xc2, 0xd6, 0xe8, 0x05, 0x1c,
0x78, 0x7e, 0xe2, 0xc6, 0x7e, 0xe0, 0x87, 0x24, 0x8d, 0xe2, 0x9c, 0x9b, 0x4d, 0x27, 0x7a, 0x0a,
0x9d, 0xd0, 0x77, 0xaf, 0x79, 0x76, 0x46, 0x4c, 0x61, 0x33, 0x66, 0xc9, 0x47, 0x92, 0x92, 0x78,
0x1e, 0x2f, 0x73, 0x4e, 0x4a, 0x07, 0x1a, 0x00, 0xca, 0x0c, 0x3e, 0x3d, 0x66, 0xf9, 0x78, 0x68,
0x71, 0x59, 0xdd, 0xb2, 0xc3, 0x4e, 0x5a, 0x46, 0x2e, 0x59, 0xb2, 0x62, 0xed, 0xec, 0x24, 0x61,
0x6b, 0x11, 0x1c, 0xed, 0xa0, 0x83, 0x81, 0x28, 0x24, 0x92, 0xff, 0xe3, 0x8a, 0x9c, 0x9f, 0x81,
0xe4, 0x2e, 0x48, 0x18, 0xd2, 0xa5, 0x59, 0x28, 0xaa, 0x70, 0xb0, 0x96, 0x5e, 0xad, 0xfd, 0xa5,
0x67, 0x8a, 0x8b, 0x20, 0x4c, 0xed, 0xbf, 0x0e, 0xc8, 0xc3, 0x05, 0xb9, 0x67, 0x50, 0x6c, 0x48,
0xab, 0xce, 0x77, 0x2a, 0xd2, 0x12, 0x63, 0x64, 0xbf, 0x32, 0x46, 0x4e, 0x40, 0x8e, 0x69, 0xb2,
0x8a, 0xc2, 0x84, 0x3a, 0x69, 0x94, 0x33, 0x0a, 0xc2, 0x65, 0x47, 0xe8, 0x33, 0xe8, 0xd0, 0x30,
0x71, 0x38, 0xdf, 0xb9, 0xcc, 0x68, 0x98, 0x4c, 0x18, 0xdd, 0x95, 0x2b, 0xdd, 0xda, 0xb8, 0xd2,
0xdb, 0xb7, 0xb3, 0xfd, 0xd0, 0xdb, 0x89, 0x46, 0xd0, 0xcd, 0x45, 0x9c, 0x65, 0x76, 0x78, 0x66,
0x45, 0x84, 0x15, 0x0e, 0x06, 0xc3, 0x2c, 0x32, 0xab, 0xe2, 0x96, 0x06, 0x7a, 0x0d, 0xed, 0x24,
0x7b, 0x9b, 0x54, 0x89, 0xab, 0x58, 0x2d, 0x0b, 0x6c, 0x3e, 0x5a, 0xe7, 0x7b, 0x58, 0x84, 0xa2,
0x01, 0x34, 0x7d, 0xd6, 0x7f, 0x15, 0x78, 0xce, 0x93, 0xad, 0xe7, 0xa6, 0xcc, 0xc8, 0xc2, 0x58,
0x3c, 0x61, 0x23, 0x5f, 0x95, 0xb7, 0xe3, 0xab, 0x4f, 0x09, 0x8b, 0xe7, 0x61, 0xe8, 0x18, 0x24,
0x37, 0x0a, 0x82, 0x75, 0xe8, 0xa7, 0x37, 0x6a, 0x97, 0xc9, 0xee, 0x7c, 0x0f, 0x97, 0x2e, 0x34,
0x84, 0x43, 0x2f, 0xd3, 0x94, 0x78, 0x81, 0x55, 0x77, 0x1b, 0xfd, 0xa6, 0xe8, 0xce, 0xf7, 0x70,
0xcf, 0xdb, 0x1c, 0x40, 0xc5, 0xb8, 0x3c, 0xa8, 0x8e, 0xcb, 0xe7, 0xd0, 0xf5, 0xfc, 0x64, 0xb5,
0x24, 0x37, 0x59, 0x23, 0x7b, 0xbc, 0x5d, 0x72, 0xee, 0xe3, 0xcd, 0xbc, 0x84, 0xe3, 0x84, 0xd1,
0xce, 0x78, 0x24, 0x6e, 0xea, 0xc4, 0xf4, 0xb7, 0x35, 0x4d, 0x52, 0x27, 0xf1, 0xaf, 0x42, 0x92,
0xae, 0x63, 0xaa, 0x1e, 0x6e, 0x0f, 0x84, 0x61, 0x16, 0x8a, 0xb3, 0x48, 0x4b, 0x04, 0xe2, 0xcf,
0x59, 0xa1, 0x1d, 0x9b, 0x28, 0x04, 0x2d, 0xa6, 0x2e, 0xf5, 0x3f, 0x52, 0xef, 0x8e, 0xb3, 0x94,
0x87, 0x9e, 0x75, 0x22, 0x8a, 0xed, 0x3a, 0xef, 0x25, 0x1c, 0x8a, 0x63, 0x04, 0xab, 0x9f, 0x9c,
0xd6, 0xfa, 0x1d, 0xdc, 0xcb, 0xdd, 0x39, 0x73, 0xda, 0x1f, 0x75, 0x90, 0x2b, 0x8a, 0x42, 0x2a,
0x3c, 0x16, 0x8f, 0xf8, 0x70, 0x3a, 0xb1, 0x8d, 0x89, 0x2d, 0x9e, 0xf1, 0x1e, 0x80, 0x6d, 0xfc,
0x62, 0x3b, 0xb3, 0x1f, 0x75, 0x73, 0xa2, 0xd4, 0x90, 0x0c, 0x6d, 0xcb, 0x36, 0x87, 0xef, 0x0c,
0xac, 0xd4, 0x11, 0x40, 0xcb, 0xb2, 0x75, 0x7b, 0x6e, 0x29, 0xfb, 0x48, 0x82, 0xa6, 0x31, 0x9e,
0xfe, 0x60, 0x2a, 0x0d, 0x74, 0x04, 0x8f, 0x6c, 0xac, 0x4f, 0x2c, 0x7d, 0x68, 0x9b, 0x53, 0x56,
0x71, 0x3c, 0xd6, 0x27, 0x23, 0xa5, 0x89, 0xfa, 0xf0, 0xc2, 0xba, 0xb0, 0x6c, 0x63, 0xec, 0x8c,
0x0d, 0xcb, 0xd2, 0xcf, 0x8c, 0xe2, 0xb4, 0x19, 0x36, 0x7f, 0xd2, 0x6d, 0xc3, 0x39, 0xc3, 0xd3,
0xf9, 0x4c, 0x69, 0xb1, 0x6a, 0xe6, 0x58, 0x3f, 0x33, 0x94, 0x36, 0x5b, 0xf2, 0x0f, 0x0b, 0xa5,
0x83, 0x0e, 0x40, 0x62, 0xc5, 0xe6, 0x13, 0xd3, 0xbe, 0x50, 0x24, 0xf6, 0xe9, 0xb1, 0x55, 0xee,
0x4c, 0x9f, 0x29, 0x80, 0x1e, 0xc1, 0x21, 0xab, 0xab, 0x0f, 0x6d, 0x07, 0x1b, 0xef, 0xe7, 0x86,
0x65, 0x2b, 0x32, 0x73, 0x8e, 0x4c, 0x6b, 0x38, 0xc5, 0x23, 0x11, 0xad, 0x74, 0xdf, 0x4a, 0xc5,
0xa7, 0x93, 0x36, 0x87, 0xa3, 0x5d, 0xb4, 0x3e, 0x03, 0xa9, 0xec, 0x56, 0xf6, 0x89, 0x55, 0x3a,
0xee, 0x9e, 0x43, 0x6f, 0x0f, 0x7e, 0x95, 0x07, 0x5f, 0x7e, 0x2b, 0x5a, 0xfb, 0xa1, 0xc5, 0x57,
0x5f, 0xfd, 0x1f, 0x00, 0x00, 0xff, 0xff, 0x5f, 0x62, 0xda, 0x69, 0x7d, 0x0a, 0x00, 0x00,
// 1212 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x56, 0xdd, 0x8e, 0xdb, 0xc4,
0x17, 0xdf, 0x7c, 0xc7, 0xc7, 0xd9, 0xac, 0xff, 0xd3, 0xfe, 0xbb, 0xa6, 0x94, 0x36, 0xb5, 0x2a,
0x35, 0x57, 0x41, 0x2a, 0x05, 0x55, 0xe2, 0x02, 0xb9, 0x89, 0xd9, 0x9a, 0x92, 0x64, 0x3b, 0x76,
0x80, 0x72, 0x13, 0x4d, 0xed, 0xd9, 0x8d, 0xb5, 0x89, 0x1d, 0xec, 0x49, 0xc5, 0x72, 0xcf, 0xe3,
0x70, 0xcb, 0x2d, 0x4f, 0xc1, 0x2b, 0xf0, 0x00, 0x88, 0x07, 0x40, 0x33, 0xf6, 0xf8, 0x23, 0x34,
0xdb, 0x5e, 0x79, 0xce, 0xf1, 0xf9, 0xf8, 0xcd, 0xef, 0x9c, 0x39, 0x33, 0x80, 0xbc, 0x15, 0x61,
0xcb, 0x0d, 0x4d, 0x12, 0x72, 0x49, 0x47, 0xdb, 0x38, 0x62, 0x11, 0xea, 0x8a, 0xcf, 0x9b, 0xdd,
0xc5, 0x5d, 0x95, 0x86, 0xbb, 0x4d, 0x92, 0xaa, 0x8d, 0x67, 0xd0, 0x77, 0x58, 0xe0, 0x5d, 0xd1,
0x78, 0x9a, 0x9a, 0x23, 0x04, 0xcd, 0x15, 0x49, 0x56, 0x7a, 0x6d, 0x50, 0x1b, 0x2a, 0x58, 0xac,
0xb9, 0x6e, 0x4b, 0xbc, 0x2b, 0xbd, 0x3e, 0xa8, 0x0d, 0x5b, 0x58, 0xac, 0x8d, 0x57, 0xd0, 0xb3,
0x37, 0xe4, 0x92, 0x4a, 0x3f, 0x1d, 0x3a, 0x5b, 0x72, 0xbd, 0x8e, 0x88, 0x2f, 0x5c, 0x7b, 0x58,
0x8a, 0xe8, 0x31, 0x34, 0xd9, 0xf5, 0x96, 0x0a, 0xef, 0xfe, 0x93, 0x5b, 0x23, 0x89, 0x64, 0x24,
0xfc, 0xdd, 0xeb, 0x2d, 0xc5, 0xc2, 0xc0, 0xf8, 0xbd, 0x06, 0x3d, 0x73, 0xe7, 0x07, 0xd1, 0xfb,
0x63, 0x3e, 0xad, 0xc4, 0x1c, 0x14, 0x31, 0xcb, 0xfe, 0xa9, 0x50, 0x24, 0x40, 0x0f, 0x40, 0xf5,
0x77, 0x31, 0x61, 0x41, 0x14, 0x2e, 0x37, 0x89, 0xde, 0x18, 0xd4, 0x86, 0x4d, 0x0c, 0x52, 0x35,
0x4d, 0x8c, 0xcf, 0x41, 0xc9, 0x7d, 0xd0, 0x1d, 0x40, 0x8b, 0xd9, 0xcb, 0xd9, 0xfc, 0xfb, 0xd9,
0xd2, 0x5c, 0x4c, 0xec, 0xf9, 0xd2, 0x7d, 0x7d, 0x6e, 0x69, 0x47, 0xa8, 0x03, 0x0d, 0xd3, 0x1c,
0x6b, 0x35, 0xb1, 0x98, 0x62, 0xad, 0x6e, 0xfc, 0x51, 0x03, 0xd5, 0xf2, 0x03, 0x26, 0x71, 0xdf,
0x86, 0x96, 0xb7, 0x8e, 0xbc, 0x2b, 0x81, 0xba, 0x89, 0x53, 0x81, 0xb3, 0xc8, 0xe8, 0xcf, 0x4c,
0x60, 0x56, 0xb0, 0x58, 0xa3, 0x53, 0xe8, 0x88, 0x62, 0x05, 0xbe, 0x40, 0xa3, 0xe0, 0x36, 0x17,
0x6d, 0x1f, 0x7d, 0x02, 0x90, 0x15, 0x90, 0xff, 0x6b, 0x8a, 0x7f, 0x4a, 0xa6, 0xb1, 0x7d, 0x9e,
0xe1, 0x32, 0x26, 0x21, 0xd3, 0x5b, 0x82, 0x97, 0x54, 0x40, 0xcf, 0xa0, 0x27, 0x9d, 0x04, 0x3b,
0x6d, 0xc1, 0xce, 0xff, 0x0b, 0x76, 0x32, 0x80, 0x82, 0x12, 0x75, 0x53, 0x08, 0xc6, 0x6f, 0x35,
0x38, 0x9e, 0xd0, 0x35, 0x65, 0xf4, 0xe6, 0x3d, 0x94, 0xf0, 0xd6, 0x6f, 0xc0, 0xdb, 0x38, 0x88,
0xb7, 0x79, 0x13, 0xde, 0xd6, 0x07, 0xe3, 0xb5, 0x01, 0xa5, 0x70, 0xbf, 0x8e, 0xe2, 0xe9, 0x7b,
0x30, 0x57, 0xa1, 0xd5, 0xf7, 0xa0, 0x19, 0x7f, 0xd6, 0xa1, 0x3f, 0x09, 0x12, 0x2f, 0x8a, 0x7d,
0x19, 0xa7, 0x0f, 0xf5, 0xc0, 0xcf, 0x4e, 0x40, 0x3d, 0xf0, 0x45, 0xe5, 0x64, 0xb7, 0x29, 0x59,
0x2f, 0xdd, 0x03, 0x85, 0x05, 0x1b, 0x9a, 0x30, 0xb2, 0xd9, 0xca, 0xfd, 0xe6, 0x0a, 0x34, 0x84,
0x93, 0x5c, 0xe0, 0x9d, 0x41, 0x65, 0x0d, 0xf7, 0xd5, 0xbc, 0xc7, 0xbd, 0x28, 0x64, 0x34, 0xab,
0xa5, 0x82, 0xa5, 0x88, 0xbe, 0x80, 0x36, 0xd9, 0xb1, 0x55, 0x14, 0x8b, 0x3a, 0xaa, 0x4f, 0xee,
0x17, 0xbc, 0x54, 0xf1, 0x9a, 0xc2, 0x0a, 0x67, 0xd6, 0xe8, 0x2b, 0x50, 0x62, 0x7a, 0x41, 0x63,
0x1a, 0x7a, 0x54, 0xef, 0x08, 0xd7, 0x87, 0x87, 0x5c, 0xb1, 0x34, 0xc4, 0x85, 0x0f, 0x9a, 0x80,
0x4a, 0x18, 0x23, 0xde, 0x6a, 0x43, 0x43, 0x96, 0xe8, 0xdd, 0x41, 0x63, 0xa8, 0x3e, 0x31, 0x0e,
0x66, 0xcf, 0x4d, 0x71, 0xd9, 0xcd, 0xf8, 0xab, 0x06, 0xb7, 0xdf, 0x85, 0xf3, 0x5d, 0xec, 0x86,
0x64, 0x93, 0xb3, 0xcb, 0xd7, 0xe8, 0x11, 0x1c, 0xfb, 0x41, 0xe2, 0xc5, 0xc1, 0x26, 0x08, 0x09,
0x8b, 0xe2, 0x8c, 0xe1, 0xaa, 0x12, 0xdd, 0x85, 0x6e, 0x18, 0x78, 0x57, 0xc2, 0x3b, 0xa5, 0x37,
0x97, 0x79, 0x7d, 0xc8, 0x5b, 0xc2, 0x48, 0xbc, 0x88, 0xd7, 0x19, 0xb3, 0x85, 0x02, 0x8d, 0x00,
0xa5, 0x82, 0x98, 0x41, 0xe7, 0xd9, 0x90, 0x69, 0x8b, 0xe6, 0x7c, 0xc7, 0x1f, 0x9e, 0x69, 0x1d,
0x79, 0x64, 0xcd, 0x83, 0x75, 0xd2, 0x4c, 0x52, 0x36, 0x22, 0x38, 0x3d, 0x40, 0x2a, 0x07, 0x91,
0x37, 0x5a, 0xb6, 0xe3, 0xd2, 0xa1, 0xb8, 0x07, 0x8a, 0xb7, 0x22, 0x61, 0x48, 0xd7, 0x76, 0xde,
0x97, 0xb9, 0x82, 0x37, 0xc6, 0xe5, 0x2e, 0x58, 0xfb, 0xb6, 0x3c, 0x4e, 0x52, 0x34, 0xfe, 0xa9,
0x81, 0x7e, 0xa8, 0x06, 0xff, 0x61, 0xb7, 0x02, 0x61, 0xbf, 0xf9, 0x91, 0x06, 0x8d, 0x5d, 0xbc,
0xce, 0x12, 0xf0, 0x25, 0xdf, 0xe9, 0x45, 0xb0, 0xa6, 0xb3, 0x12, 0xa7, 0x52, 0xe6, 0x55, 0xe1,
0x6b, 0x27, 0xf8, 0x85, 0x3e, 0xbf, 0x66, 0x34, 0x11, 0xbc, 0x36, 0x71, 0x55, 0x89, 0x06, 0xa0,
0x66, 0x2d, 0xec, 0xca, 0x21, 0xa4, 0xe0, 0xb2, 0xaa, 0x3c, 0xd7, 0x3b, 0xd5, 0xb9, 0x5e, 0xe6,
0xb9, 0xbb, 0xc7, 0xf3, 0xdf, 0x5d, 0x50, 0xc7, 0x2b, 0xf2, 0x9e, 0x29, 0x5b, 0x39, 0x97, 0x75,
0xf1, 0xa7, 0x74, 0x2e, 0xe5, 0x0c, 0x6e, 0x94, 0x66, 0xf0, 0x03, 0x50, 0x63, 0x9a, 0x6c, 0xa3,
0x30, 0xa1, 0x4b, 0x16, 0x65, 0x9b, 0x06, 0xa9, 0x72, 0x23, 0xf4, 0x11, 0x74, 0x69, 0x98, 0x2c,
0x45, 0x9b, 0x65, 0x67, 0x94, 0x86, 0x89, 0x60, 0xa4, 0x34, 0x0f, 0xdb, 0x95, 0x79, 0xb8, 0x3f,
0xda, 0x3a, 0x1f, 0x3a, 0xda, 0xd0, 0x04, 0x7a, 0x19, 0x57, 0xa9, 0x67, 0x57, 0x78, 0x96, 0x4e,
0x70, 0x89, 0x83, 0xd1, 0xb8, 0x60, 0xb5, 0x4a, 0xf1, 0x53, 0xe8, 0x24, 0xe9, 0xc5, 0xae, 0x2b,
0x62, 0x04, 0xe8, 0x45, 0x80, 0xea, 0x8d, 0xff, 0xe2, 0x08, 0x4b, 0x53, 0x34, 0x82, 0x56, 0xc0,
0xdb, 0x5e, 0x07, 0xe1, 0x73, 0x67, 0xef, 0xae, 0x2e, 0x3c, 0x52, 0x33, 0x6e, 0x4f, 0xf8, 0x7d,
0xa9, 0xab, 0xfb, 0xf6, 0xe5, 0x7b, 0x98, 0xdb, 0x0b, 0x33, 0x74, 0x1f, 0x14, 0x2f, 0xda, 0x6c,
0x76, 0x61, 0xc0, 0xae, 0xf5, 0x1e, 0x2f, 0xfd, 0x8b, 0x23, 0x5c, 0xa8, 0xd0, 0x18, 0x4e, 0xfc,
0xb4, 0xb1, 0xe5, 0xf3, 0x45, 0xf7, 0xf6, 0xd1, 0x57, 0x3b, 0xff, 0xc5, 0x11, 0xee, 0xfb, 0xd5,
0xe9, 0x9d, 0xdf, 0x35, 0xc7, 0xe5, 0xbb, 0xe6, 0x21, 0xf4, 0xfc, 0x20, 0xd9, 0xae, 0xc9, 0x75,
0x5a, 0xc8, 0x7e, 0xda, 0x96, 0x99, 0x4e, 0x14, 0xf3, 0x02, 0xee, 0x27, 0x9c, 0x76, 0xce, 0x23,
0xf1, 0xd8, 0x32, 0xa6, 0x3f, 0xed, 0x68, 0xc2, 0x96, 0x49, 0x70, 0x19, 0x12, 0xb6, 0x8b, 0xa9,
0x7e, 0xb2, 0x3f, 0x4d, 0xc7, 0xa9, 0x29, 0x4e, 0x2d, 0x1d, 0x69, 0x88, 0x3f, 0xe6, 0x81, 0x0e,
0xfc, 0x44, 0x21, 0x18, 0x31, 0xf5, 0x68, 0xf0, 0x96, 0xfa, 0x37, 0xe4, 0xd2, 0x3e, 0x34, 0xd7,
0x03, 0x19, 0xec, 0x50, 0xbe, 0xc7, 0x70, 0x22, 0xd3, 0x48, 0x56, 0xff, 0x37, 0xa8, 0x0d, 0xbb,
0xb8, 0x9f, 0xa9, 0x33, 0xe6, 0x8c, 0x5f, 0xeb, 0xa0, 0x8e, 0x2b, 0xe7, 0xf4, 0xb6, 0x7c, 0x01,
0x8d, 0xe7, 0x33, 0xd7, 0x9a, 0xb9, 0xf2, 0x0d, 0xd4, 0x07, 0x70, 0xad, 0x1f, 0xdc, 0xe5, 0xf9,
0xb7, 0xa6, 0x3d, 0xd3, 0x6a, 0x48, 0x85, 0x8e, 0xe3, 0xda, 0xe3, 0x97, 0x16, 0xd6, 0xea, 0x08,
0xa0, 0xed, 0xb8, 0xa6, 0xbb, 0x70, 0xb4, 0x06, 0x52, 0xa0, 0x65, 0x4d, 0xe7, 0xdf, 0xd8, 0x5a,
0x13, 0x9d, 0xc2, 0x2d, 0x17, 0x9b, 0x33, 0xc7, 0x1c, 0xbb, 0xf6, 0x9c, 0x47, 0x9c, 0x4e, 0xcd,
0xd9, 0x44, 0x6b, 0xa1, 0x21, 0x3c, 0x72, 0x5e, 0x3b, 0xae, 0x35, 0x5d, 0x4e, 0x2d, 0xc7, 0x31,
0xcf, 0xac, 0x3c, 0xdb, 0x39, 0xb6, 0xbf, 0x33, 0x5d, 0x6b, 0x79, 0x86, 0xe7, 0x8b, 0x73, 0xad,
0xcd, 0xa3, 0xd9, 0x53, 0xf3, 0xcc, 0xd2, 0x3a, 0x7c, 0x29, 0x5e, 0x65, 0x5a, 0x17, 0x1d, 0x83,
0xc2, 0x83, 0x2d, 0x66, 0xb6, 0xfb, 0x5a, 0x53, 0xf8, 0xbb, 0x6d, 0x2f, 0xdc, 0x99, 0x79, 0xae,
0x01, 0xba, 0x05, 0x27, 0x3c, 0xae, 0x39, 0x76, 0x97, 0xd8, 0x7a, 0xb5, 0xb0, 0x1c, 0x57, 0x53,
0xb9, 0x72, 0x62, 0x3b, 0xe3, 0x39, 0x9e, 0x48, 0x6b, 0xad, 0xf7, 0x5c, 0xc9, 0xe7, 0x93, 0xb1,
0x80, 0xd3, 0x43, 0xb4, 0xde, 0x03, 0xa5, 0xa8, 0x56, 0xfa, 0x3e, 0x2d, 0x14, 0x37, 0xcf, 0xa1,
0xe7, 0xc7, 0x3f, 0xaa, 0xa3, 0x4f, 0xbf, 0x94, 0xa5, 0x7d, 0xd3, 0x16, 0xab, 0xcf, 0xfe, 0x0d,
0x00, 0x00, 0xff, 0xff, 0xc9, 0x29, 0xc8, 0x51, 0xba, 0x0b, 0x00, 0x00,
}

View File

@ -67,6 +67,7 @@ message DiscordMessage {
string content = 5;
DiscordMessageAuthor author = 6;
DiscordMessageReference reference = 7;
repeated DiscordMessageAttachment attachments = 8;
}
message DiscordMessageAuthor {
@ -85,6 +86,17 @@ message DiscordMessageReference {
string guildId = 3;
}
message DiscordMessageAttachment {
string id = 1;
string messageId = 2;
string url = 3;
string fileName = 4;
uint64 fileSizeBytes = 5;
string contentType = 6;
bytes payload = 7;
string localUrl = 8;
}
message ChatMessage {
// Lamport timestamp of the chat message
uint64 clock = 1;