From d1fae5335a8394253417261a2ccce970348092b5 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Fri, 24 Feb 2023 19:05:50 +0200 Subject: [PATCH] Add trace metadata to IPC requests --- imessage/interface.go | 38 +++++++++------- imessage/ios/ipc.go | 96 +++++++++++++++++++++++++++++++--------- imessage/ios/requests.go | 33 ++++++++++++++ imessage/mac/database.go | 9 ++-- imessage/mac/groups.go | 4 +- imessage/mac/messages.go | 14 +++--- imessage/mac/send.go | 12 ++--- matrix.go | 9 ++-- portal.go | 24 ++++++++-- 9 files changed, 175 insertions(+), 64 deletions(-) diff --git a/imessage/interface.go b/imessage/interface.go index 5909a4f5..74c6ef35 100644 --- a/imessage/interface.go +++ b/imessage/interface.go @@ -39,13 +39,17 @@ type ContactAPI interface { GetContactList() ([]*Contact, error) } +type ExtraParams struct { + TraceData map[string]string +} + type API interface { Start(readyCallback func()) error Stop() - GetMessagesSinceDate(chatID string, minDate time.Time, backfillID string) ([]*Message, error) - GetMessagesWithLimit(chatID string, limit int, backfillID string) ([]*Message, error) - GetChatsWithMessagesAfter(minDate time.Time) ([]ChatIdentifier, error) - GetMessage(guid string) (*Message, error) + GetMessagesSinceDate(chatID string, minDate time.Time, backfillID string, extra ...ExtraParams) ([]*Message, error) + GetMessagesWithLimit(chatID string, limit int, backfillID string, extra ...ExtraParams) ([]*Message, error) + GetChatsWithMessagesAfter(minDate time.Time, extra ...ExtraParams) ([]ChatIdentifier, error) + GetMessage(guid string, extra ...ExtraParams) (*Message, error) MessageChan() <-chan *Message ReadReceiptChan() <-chan *ReadReceipt TypingNotificationChan() <-chan *TypingNotification @@ -54,22 +58,22 @@ type API interface { MessageStatusChan() <-chan *SendMessageStatus BackfillTaskChan() <-chan *BackfillTask ContactAPI - GetChatInfo(chatID, threadID string) (*ChatInfo, error) - GetGroupAvatar(chatID string) (*Attachment, error) + GetChatInfo(chatID, threadID string, extra ...ExtraParams) (*ChatInfo, error) + GetGroupAvatar(chatID string, extra ...ExtraParams) (*Attachment, error) - ResolveIdentifier(identifier string) (string, error) - PrepareDM(guid string) error + ResolveIdentifier(identifier string, extra ...ExtraParams) (string, error) + PrepareDM(guid string, extra ...ExtraParams) error - SendMessage(chatID, text string, replyTo string, replyToPart int, richLink *RichLink, metadata MessageMetadata) (*SendResponse, error) - SendFile(chatID, text, filename string, pathOnDisk string, replyTo string, replyToPart int, mimeType string, voiceMemo bool, metadata MessageMetadata) (*SendResponse, error) + SendMessage(chatID, text string, replyTo string, replyToPart int, richLink *RichLink, metadata MessageMetadata, extra ...ExtraParams) (*SendResponse, error) + SendFile(chatID, text, filename string, pathOnDisk string, replyTo string, replyToPart int, mimeType string, voiceMemo bool, metadata MessageMetadata, extra ...ExtraParams) (*SendResponse, error) SendFileCleanup(sendFileDir string) - SendTapback(chatID, targetGUID string, targetPart int, tapback TapbackType, remove bool) (*SendResponse, error) - SendReadReceipt(chatID, readUpTo string) error - SendTypingNotification(chatID string, typing bool) error - SendMessageBridgeResult(chatID, messageID string, eventID id.EventID, success bool) - SendBackfillResult(chatID, backfillID string, success bool, idMap map[string][]id.EventID) - SendChatBridgeResult(guid string, mxid id.RoomID) - NotifyUpcomingMessage(eventID id.EventID) + SendTapback(chatID, targetGUID string, targetPart int, tapback TapbackType, remove bool, extra ...ExtraParams) (*SendResponse, error) + SendReadReceipt(chatID, readUpTo string, extra ...ExtraParams) error + SendTypingNotification(chatID string, typing bool, extra ...ExtraParams) error + SendMessageBridgeResult(chatID, messageID string, eventID id.EventID, success bool, extra ...ExtraParams) + SendBackfillResult(chatID, backfillID string, success bool, idMap map[string][]id.EventID, extra ...ExtraParams) + SendChatBridgeResult(guid string, mxid id.RoomID, extra ...ExtraParams) + NotifyUpcomingMessage(eventID id.EventID, extra ...ExtraParams) PreStartupSyncHook() (StartupSyncHookResponse, error) PostStartupSyncHook() diff --git a/imessage/ios/ipc.go b/imessage/ios/ipc.go index aa6acf80..20a19ef3 100644 --- a/imessage/ios/ipc.go +++ b/imessage/ios/ipc.go @@ -31,6 +31,13 @@ import ( "go.mau.fi/mautrix-imessage/ipc" ) +func firstExtraParam(params []imessage.ExtraParams) (param imessage.ExtraParams) { + if len(params) > 0 { + param = params[0] + } + return +} + const ( IncomingMessage ipc.Command = "message" IncomingReadReceipt ipc.Command = "read_receipt" @@ -374,12 +381,14 @@ func (ios *iOSConnector) handleIncomingBackfillTask(data json.RawMessage) interf return nil } -func (ios *iOSConnector) GetMessagesSinceDate(chatID string, minDate time.Time, backfillID string) ([]*imessage.Message, error) { +func (ios *iOSConnector) GetMessagesSinceDate(chatID string, minDate time.Time, backfillID string, extra ...imessage.ExtraParams) ([]*imessage.Message, error) { resp := make([]*imessage.Message, 0) err := ios.IPC.Request(context.Background(), ReqGetMessagesAfter, &GetMessagesAfterRequest{ ChatGUID: chatID, Timestamp: timeToFloat(minDate), BackfillID: backfillID, + + TraceMeta: firstExtraParam(extra).TraceData, }, &resp) for _, msg := range resp { ios.postprocessMessage(msg, "messages since date") @@ -387,12 +396,14 @@ func (ios *iOSConnector) GetMessagesSinceDate(chatID string, minDate time.Time, return resp, err } -func (ios *iOSConnector) GetMessagesWithLimit(chatID string, limit int, backfillID string) ([]*imessage.Message, error) { +func (ios *iOSConnector) GetMessagesWithLimit(chatID string, limit int, backfillID string, extra ...imessage.ExtraParams) ([]*imessage.Message, error) { resp := make([]*imessage.Message, 0) err := ios.IPC.Request(context.Background(), ReqGetRecentMessages, &GetRecentMessagesRequest{ ChatGUID: chatID, Limit: limit, BackfillID: backfillID, + + TraceMeta: firstExtraParam(extra).TraceData, }, &resp) for _, msg := range resp { ios.postprocessMessage(msg, "messages with limit") @@ -400,15 +411,19 @@ func (ios *iOSConnector) GetMessagesWithLimit(chatID string, limit int, backfill return resp, err } -func (ios *iOSConnector) GetMessage(guid string) (resp *imessage.Message, err error) { +func (ios *iOSConnector) GetMessage(guid string, extra ...imessage.ExtraParams) (resp *imessage.Message, err error) { return resp, ios.IPC.Request(context.Background(), ReqGetMessage, &GetMessageRequest{ GUID: guid, + + TraceMeta: firstExtraParam(extra).TraceData, }, &resp) } -func (ios *iOSConnector) GetChatsWithMessagesAfter(minDate time.Time) (resp []imessage.ChatIdentifier, err error) { +func (ios *iOSConnector) GetChatsWithMessagesAfter(minDate time.Time, extra ...imessage.ExtraParams) (resp []imessage.ChatIdentifier, err error) { return resp, ios.IPC.Request(context.Background(), ReqGetChats, &GetChatsRequest{ MinTimestamp: timeToFloat(minDate), + + TraceMeta: firstExtraParam(extra).TraceData, }, &resp) } @@ -461,19 +476,28 @@ func (ios *iOSConnector) GetContactList() ([]*imessage.Contact, error) { return resp.Contacts, err } -func (ios *iOSConnector) GetChatInfo(chatID, threadID string) (*imessage.ChatInfo, error) { +func (ios *iOSConnector) GetChatInfo(chatID, threadID string, extra ...imessage.ExtraParams) (*imessage.ChatInfo, error) { var resp imessage.ChatInfo - err := ios.IPC.Request(context.Background(), ReqGetChat, &GetChatRequest{ChatGUID: chatID, ThreadID: threadID}, &resp) + err := ios.IPC.Request(context.Background(), ReqGetChat, &GetChatRequest{ + ChatGUID: chatID, + ThreadID: threadID, + + TraceMeta: firstExtraParam(extra).TraceData, + }, &resp) return &resp, err } -func (ios *iOSConnector) GetGroupAvatar(chatID string) (*imessage.Attachment, error) { +func (ios *iOSConnector) GetGroupAvatar(chatID string, extra ...imessage.ExtraParams) (*imessage.Attachment, error) { var resp imessage.Attachment - err := ios.IPC.Request(context.Background(), ReqGetChatAvatar, &GetChatRequest{ChatGUID: chatID}, &resp) + err := ios.IPC.Request(context.Background(), ReqGetChatAvatar, &GetChatRequest{ + ChatGUID: chatID, + + TraceMeta: firstExtraParam(extra).TraceData, + }, &resp) return &resp, err } -func (ios *iOSConnector) SendMessage(chatID, text string, replyTo string, replyToPart int, richLink *imessage.RichLink, metadata imessage.MessageMetadata) (*imessage.SendResponse, error) { +func (ios *iOSConnector) SendMessage(chatID, text string, replyTo string, replyToPart int, richLink *imessage.RichLink, metadata imessage.MessageMetadata, extras ...imessage.ExtraParams) (*imessage.SendResponse, error) { var resp imessage.SendResponse err := ios.IPC.Request(context.Background(), ReqSendMessage, &SendMessageRequest{ ChatGUID: chatID, @@ -482,6 +506,8 @@ func (ios *iOSConnector) SendMessage(chatID, text string, replyTo string, replyT ReplyToPart: replyToPart, RichLink: richLink, Metadata: metadata, + + TraceMeta: firstExtraParam(extras).TraceData, }, &resp) if err == nil { var warn bool @@ -496,7 +522,7 @@ func (ios *iOSConnector) SendMessage(chatID, text string, replyTo string, replyT return &resp, err } -func (ios *iOSConnector) SendFile(chatID, text, filename string, pathOnDisk string, replyTo string, replyToPart int, mimeType string, voiceMemo bool, metadata imessage.MessageMetadata) (*imessage.SendResponse, error) { +func (ios *iOSConnector) SendFile(chatID, text, filename string, pathOnDisk string, replyTo string, replyToPart int, mimeType string, voiceMemo bool, metadata imessage.MessageMetadata, extra ...imessage.ExtraParams) (*imessage.SendResponse, error) { var resp imessage.SendResponse err := ios.IPC.Request(context.Background(), ReqSendMedia, &SendMediaRequest{ ChatGUID: chatID, @@ -510,6 +536,8 @@ func (ios *iOSConnector) SendFile(chatID, text, filename string, pathOnDisk stri ReplyToPart: replyToPart, IsAudioMessage: voiceMemo, Metadata: metadata, + + TraceMeta: firstExtraParam(extra).TraceData, }, &resp) if err == nil { var warn bool @@ -525,7 +553,7 @@ func (ios *iOSConnector) SendFileCleanup(sendFileDir string) { _ = os.RemoveAll(sendFileDir) } -func (ios *iOSConnector) SendTapback(chatID, targetGUID string, targetPart int, tapback imessage.TapbackType, remove bool) (*imessage.SendResponse, error) { +func (ios *iOSConnector) SendTapback(chatID, targetGUID string, targetPart int, tapback imessage.TapbackType, remove bool, extra ...imessage.ExtraParams) (*imessage.SendResponse, error) { if remove { tapback += imessage.TapbackRemoveOffset } @@ -535,6 +563,8 @@ func (ios *iOSConnector) SendTapback(chatID, targetGUID string, targetPart int, TargetGUID: targetGUID, TargetPart: targetPart, Type: tapback, + + TraceMeta: firstExtraParam(extra).TraceData, }, &resp) if err != nil { return nil, err @@ -542,21 +572,25 @@ func (ios *iOSConnector) SendTapback(chatID, targetGUID string, targetPart int, return &resp, err } -func (ios *iOSConnector) SendReadReceipt(chatID, readUpTo string) error { +func (ios *iOSConnector) SendReadReceipt(chatID, readUpTo string, extra ...imessage.ExtraParams) error { return ios.IPC.Send(ReqSendReadReceipt, &SendReadReceiptRequest{ ChatGUID: chatID, ReadUpTo: readUpTo, + + TraceMeta: firstExtraParam(extra).TraceData, }) } -func (ios *iOSConnector) SendTypingNotification(chatID string, typing bool) error { +func (ios *iOSConnector) SendTypingNotification(chatID string, typing bool, extra ...imessage.ExtraParams) error { return ios.IPC.Send(ReqSetTyping, &SetTypingRequest{ ChatGUID: chatID, Typing: typing, + + TraceMeta: firstExtraParam(extra).TraceData, }) } -func (ios *iOSConnector) SendMessageBridgeResult(chatID, messageID string, eventID id.EventID, success bool) { +func (ios *iOSConnector) SendMessageBridgeResult(chatID, messageID string, eventID id.EventID, success bool, extra ...imessage.ExtraParams) { if !ios.isAndroid { // Only android needs message bridging confirmations return @@ -566,10 +600,12 @@ func (ios *iOSConnector) SendMessageBridgeResult(chatID, messageID string, event GUID: messageID, EventID: eventID, Success: success, + + TraceMeta: firstExtraParam(extra).TraceData, }) } -func (ios *iOSConnector) SendBackfillResult(chatID, backfillID string, success bool, idMap map[string][]id.EventID) { +func (ios *iOSConnector) SendBackfillResult(chatID, backfillID string, success bool, idMap map[string][]id.EventID, extra ...imessage.ExtraParams) { if !ios.isAndroid { // Only android needs message bridging confirmations return @@ -582,22 +618,30 @@ func (ios *iOSConnector) SendBackfillResult(chatID, backfillID string, success b BackfillID: backfillID, Success: success, MessageIDs: idMap, + + TraceMeta: firstExtraParam(extra).TraceData, }) } -func (ios *iOSConnector) SendChatBridgeResult(guid string, mxid id.RoomID) { +func (ios *iOSConnector) SendChatBridgeResult(guid string, mxid id.RoomID, extra ...imessage.ExtraParams) { _ = ios.IPC.Send(ReqChatBridgeResult, &ChatBridgeResult{ ChatGUID: guid, MXID: mxid, + + TraceMeta: firstExtraParam(extra).TraceData, }) } -func (ios *iOSConnector) NotifyUpcomingMessage(eventID id.EventID) { +func (ios *iOSConnector) NotifyUpcomingMessage(eventID id.EventID, extra ...imessage.ExtraParams) { if !ios.isAndroid { // Only android needs to be notified about upcoming messages to stay awake return } - _ = ios.IPC.Send(ReqUpcomingMessage, &UpcomingMessage{EventID: eventID}) + _ = ios.IPC.Send(ReqUpcomingMessage, &UpcomingMessage{ + EventID: eventID, + + TraceMeta: firstExtraParam(extra).TraceData, + }) } func (ios *iOSConnector) PreStartupSyncHook() (resp imessage.StartupSyncHookResponse, err error) { @@ -609,7 +653,7 @@ func (ios *iOSConnector) PostStartupSyncHook() { _ = ios.IPC.Send(ReqPostStartupSync, nil) } -func (ios *iOSConnector) ResolveIdentifier(identifier string) (string, error) { +func (ios *iOSConnector) ResolveIdentifier(identifier string, extra ...imessage.ExtraParams) (string, error) { if ios.isAndroid { return imessage.Identifier{ LocalID: identifier, @@ -617,17 +661,25 @@ func (ios *iOSConnector) ResolveIdentifier(identifier string) (string, error) { IsGroup: false, }.String(), nil } - req := ResolveIdentifierRequest{Identifier: identifier} + req := ResolveIdentifierRequest{ + Identifier: identifier, + + TraceMeta: firstExtraParam(extra).TraceData, + } var resp ResolveIdentifierResponse err := ios.IPC.Request(context.Background(), ReqResolveIdentifier, &req, &resp) return resp.GUID, err } -func (ios *iOSConnector) PrepareDM(guid string) error { +func (ios *iOSConnector) PrepareDM(guid string, extra ...imessage.ExtraParams) error { if ios.isAndroid { return nil } - return ios.IPC.Request(context.Background(), ReqPrepareDM, &PrepareDMRequest{GUID: guid}, nil) + return ios.IPC.Request(context.Background(), ReqPrepareDM, &PrepareDMRequest{ + GUID: guid, + + TraceMeta: firstExtraParam(extra).TraceData, + }, nil) } func (ios *iOSConnector) Capabilities() imessage.ConnectorCapabilities { diff --git a/imessage/ios/requests.go b/imessage/ios/requests.go index e095e426..75f5db23 100644 --- a/imessage/ios/requests.go +++ b/imessage/ios/requests.go @@ -54,6 +54,7 @@ type SendMessageRequest struct { ReplyToPart int `json:"reply_to_part"` RichLink *imessage.RichLink `json:"rich_link,omitempty"` Metadata imessage.MessageMetadata `json:"metadata,omitempty"` + TraceMeta map[string]string `json:"trace_metadata,omitempty"` } type SendMediaRequest struct { @@ -64,6 +65,7 @@ type SendMediaRequest struct { ReplyToPart int `json:"reply_to_part"` IsAudioMessage bool `json:"is_audio_message"` Metadata imessage.MessageMetadata `json:"metadata,omitempty"` + TraceMeta map[string]string `json:"trace_metadata,omitempty"` } type SendTapbackRequest struct { @@ -71,49 +73,68 @@ type SendTapbackRequest struct { TargetGUID string `json:"target_guid"` TargetPart int `json:"target_part"` Type imessage.TapbackType `json:"type"` + TraceMeta map[string]string `json:"trace_metadata,omitempty"` } type SendReadReceiptRequest struct { ChatGUID string `json:"chat_guid"` ReadUpTo string `json:"read_up_to"` + + TraceMeta map[string]string `json:"trace_metadata,omitempty"` } type SetTypingRequest struct { ChatGUID string `json:"chat_guid"` Typing bool `json:"typing"` + + TraceMeta map[string]string `json:"trace_metadata,omitempty"` } type GetChatRequest struct { ChatGUID string `json:"chat_guid"` ThreadID string `json:"thread_id"` + + TraceMeta map[string]string `json:"trace_metadata,omitempty"` } type GetChatsRequest struct { MinTimestamp float64 `json:"min_timestamp"` + + TraceMeta map[string]string `json:"trace_metadata,omitempty"` } type GetContactRequest struct { UserGUID string `json:"user_guid"` + + TraceMeta map[string]string `json:"trace_metadata,omitempty"` } type GetContactListResponse struct { Contacts []*imessage.Contact `json:"contacts"` + + TraceMeta map[string]string `json:"trace_metadata,omitempty"` } type GetRecentMessagesRequest struct { ChatGUID string `json:"chat_guid"` Limit int `json:"limit"` BackfillID string `json:"backfill_id"` + + TraceMeta map[string]string `json:"trace_metadata,omitempty"` } type GetMessageRequest struct { GUID string `json:"guid"` + + TraceMeta map[string]string `json:"trace_metadata,omitempty"` } type GetMessagesAfterRequest struct { ChatGUID string `json:"chat_guid"` Timestamp float64 `json:"timestamp"` BackfillID string `json:"backfill_id"` + + TraceMeta map[string]string `json:"trace_metadata,omitempty"` } type PingServerResponse struct { @@ -124,6 +145,8 @@ type PingServerResponse struct { type ResolveIdentifierRequest struct { Identifier string `json:"identifier"` + + TraceMeta map[string]string `json:"trace_metadata,omitempty"` } type ResolveIdentifierResponse struct { @@ -132,6 +155,8 @@ type ResolveIdentifierResponse struct { type PrepareDMRequest struct { GUID string `json:"guid"` + + TraceMeta map[string]string `json:"trace_metadata,omitempty"` } type MessageBridgeResult struct { @@ -139,11 +164,15 @@ type MessageBridgeResult struct { GUID string `json:"message_guid"` EventID id.EventID `json:"event_id,omitempty"` Success bool `json:"success"` + + TraceMeta map[string]string `json:"trace_metadata,omitempty"` } type ChatBridgeResult struct { ChatGUID string `json:"chat_guid"` MXID id.RoomID `json:"mxid"` + + TraceMeta map[string]string `json:"trace_metadata,omitempty"` } type BackfillResult struct { @@ -152,8 +181,12 @@ type BackfillResult struct { Success bool `json:"success"` MessageIDs map[string][]id.EventID `json:"message_ids"` + + TraceMeta map[string]string `json:"trace_metadata,omitempty"` } type UpcomingMessage struct { EventID id.EventID `json:"event_id"` + + TraceMeta map[string]string `json:"trace_metadata,omitempty"` } diff --git a/imessage/mac/database.go b/imessage/mac/database.go index a7a8d58b..d93a4e0a 100644 --- a/imessage/mac/database.go +++ b/imessage/mac/database.go @@ -92,18 +92,19 @@ func (mac *macOSDatabase) PreStartupSyncHook() (resp imessage.StartupSyncHookRes func (mac *macOSDatabase) PostStartupSyncHook() {} -func (mac *macOSDatabase) PrepareDM(guid string) error { +func (mac *macOSDatabase) PrepareDM(_ string, _ ...imessage.ExtraParams) error { // Nothing needed here return nil } -func (mac *macOSDatabase) SendMessageBridgeResult(chatID, messageID string, eventID id.EventID, success bool) { +func (mac *macOSDatabase) SendMessageBridgeResult(chatID, messageID string, eventID id.EventID, success bool, extra ...imessage.ExtraParams) { } -func (mac *macOSDatabase) SendBackfillResult(chatID, backfillID string, success bool, idMap map[string][]id.EventID) { +func (mac *macOSDatabase) SendBackfillResult(chatID, backfillID string, success bool, idMap map[string][]id.EventID, extra ...imessage.ExtraParams) { } -func (mac *macOSDatabase) SendChatBridgeResult(guid string, mxid id.RoomID) {} +func (mac *macOSDatabase) SendChatBridgeResult(guid string, mxid id.RoomID, extra ...imessage.ExtraParams) { +} func (mac *macOSDatabase) Capabilities() imessage.ConnectorCapabilities { return imessage.ConnectorCapabilities{ diff --git a/imessage/mac/groups.go b/imessage/mac/groups.go index f2a5dcd7..7620d5ce 100644 --- a/imessage/mac/groups.go +++ b/imessage/mac/groups.go @@ -18,6 +18,8 @@ package mac import ( "fmt" + + "go.mau.fi/mautrix-imessage/imessage" ) const groupMemberQuery = ` @@ -36,7 +38,7 @@ func (mac *macOSDatabase) prepareGroups() error { return nil } -func (mac *macOSDatabase) GetGroupMembers(chatID string) ([]string, error) { +func (mac *macOSDatabase) GetGroupMembers(chatID string, _ ...imessage.ExtraParams) ([]string, error) { res, err := mac.groupMemberQuery.Query(chatID) if err != nil { return nil, fmt.Errorf("error querying group members: %w", err) diff --git a/imessage/mac/messages.go b/imessage/mac/messages.go index a015c49c..4f614b69 100644 --- a/imessage/mac/messages.go +++ b/imessage/mac/messages.go @@ -303,7 +303,7 @@ func columnExists(db *sql.DB, table, column string) bool { return name == column } -func (mac *macOSDatabase) GetMessagesWithLimit(chatID string, limit int, backfillID string) ([]*imessage.Message, error) { +func (mac *macOSDatabase) GetMessagesWithLimit(chatID string, limit int, _ string, _ ...imessage.ExtraParams) ([]*imessage.Message, error) { res, err := mac.limitedMessagesQuery.Query(chatID, limit) if err != nil { return nil, fmt.Errorf("error querying messages with limit: %w", err) @@ -316,7 +316,7 @@ func (mac *macOSDatabase) GetMessagesWithLimit(chatID string, limit int, backfil return messages, err } -func (mac *macOSDatabase) GetMessagesSinceDate(chatID string, minDate time.Time, _ string) ([]*imessage.Message, error) { +func (mac *macOSDatabase) GetMessagesSinceDate(chatID string, minDate time.Time, _ string, _ ...imessage.ExtraParams) ([]*imessage.Message, error) { res, err := mac.messagesQuery.Query(chatID, minDate.UnixNano()-imessage.AppleEpoch.UnixNano()) if err != nil { return nil, fmt.Errorf("error querying messages after date: %w", err) @@ -324,7 +324,7 @@ func (mac *macOSDatabase) GetMessagesSinceDate(chatID string, minDate time.Time, return mac.scanMessages(res) } -func (mac *macOSDatabase) GetMessage(guid string) (*imessage.Message, error) { +func (mac *macOSDatabase) GetMessage(guid string, _ ...imessage.ExtraParams) (*imessage.Message, error) { res, err := mac.singleMessageQuery.Query(guid) if err != nil { return nil, fmt.Errorf("error querying single message: %w", err) @@ -392,7 +392,7 @@ func (mac *macOSDatabase) getReadReceiptsSince(minDate time.Time) ([]*imessage.R return receipts, minDate, nil } -func (mac *macOSDatabase) GetChatsWithMessagesAfter(minDate time.Time) ([]imessage.ChatIdentifier, error) { +func (mac *macOSDatabase) GetChatsWithMessagesAfter(minDate time.Time, _ ...imessage.ExtraParams) ([]imessage.ChatIdentifier, error) { res, err := mac.recentChatsQuery.Query(minDate.UnixNano() - imessage.AppleEpoch.UnixNano()) if err != nil { return nil, fmt.Errorf("error querying chats with messages after date: %w", err) @@ -409,7 +409,7 @@ func (mac *macOSDatabase) GetChatsWithMessagesAfter(minDate time.Time) ([]imessa return chats, nil } -func (mac *macOSDatabase) GetChatInfo(chatID, _ string) (*imessage.ChatInfo, error) { +func (mac *macOSDatabase) GetChatInfo(chatID, _ string, _ ...imessage.ExtraParams) (*imessage.ChatInfo, error) { row := mac.chatQuery.QueryRow(chatID) var info imessage.ChatInfo info.Identifier = imessage.ParseIdentifier(chatID) @@ -423,7 +423,7 @@ func (mac *macOSDatabase) GetChatInfo(chatID, _ string) (*imessage.ChatInfo, err return &info, err } -func (mac *macOSDatabase) ResolveIdentifier(identifier string) (guid string, err error) { +func (mac *macOSDatabase) ResolveIdentifier(identifier string, _ ...imessage.ExtraParams) (guid string, err error) { err = mac.chatGUIDQuery.QueryRow("%;-;" + identifier).Scan(&guid) if errors.Is(err, sql.ErrNoRows) { return "", fmt.Errorf("user not found") @@ -431,7 +431,7 @@ func (mac *macOSDatabase) ResolveIdentifier(identifier string) (guid string, err return } -func (mac *macOSDatabase) GetGroupAvatar(chatID string) (*imessage.Attachment, error) { +func (mac *macOSDatabase) GetGroupAvatar(chatID string, _ ...imessage.ExtraParams) (*imessage.Attachment, error) { if mac.groupActionQuery == nil { return nil, nil } diff --git a/imessage/mac/send.go b/imessage/mac/send.go index 5c634f0d..7ba338d9 100644 --- a/imessage/mac/send.go +++ b/imessage/mac/send.go @@ -178,11 +178,11 @@ func (mac *macOSDatabase) sendMessageWithRetry(script, fallbackScript1, fallback return err } -func (mac *macOSDatabase) SendMessage(chatID, text string, replyTo string, replyToPart int, _ *imessage.RichLink, metadata imessage.MessageMetadata) (*imessage.SendResponse, error) { +func (mac *macOSDatabase) SendMessage(chatID, text string, replyTo string, replyToPart int, _ *imessage.RichLink, metadata imessage.MessageMetadata, _ ...imessage.ExtraParams) (*imessage.SendResponse, error) { return nil, mac.sendMessageWithRetry(sendMessage, sendMessageWithService, sendMessageBuddy, imessage.ParseIdentifier(chatID), text) } -func (mac *macOSDatabase) SendFile(chatID, text, filename string, pathOnDisk string, replyTo string, replyToPart int, mimeType string, voiceMemo bool, metadata imessage.MessageMetadata) (*imessage.SendResponse, error) { +func (mac *macOSDatabase) SendFile(chatID, text, filename string, pathOnDisk string, replyTo string, replyToPart int, mimeType string, voiceMemo bool, metadata imessage.MessageMetadata, _ ...imessage.ExtraParams) (*imessage.SendResponse, error) { if voiceMemo { mac.log.Warn("received a request to send a file as a voice memo, but mac does not support this. sending as regular attachment.") } @@ -198,18 +198,18 @@ func (mac *macOSDatabase) SendFileCleanup(sendFileDir string) { }() } -func (mac *macOSDatabase) SendTapback(chatID, targetGUID string, targetPart int, tapback imessage.TapbackType, remove bool) (*imessage.SendResponse, error) { +func (mac *macOSDatabase) SendTapback(chatID, targetGUID string, targetPart int, tapback imessage.TapbackType, remove bool, _ ...imessage.ExtraParams) (*imessage.SendResponse, error) { return nil, nil } -func (mac *macOSDatabase) SendReadReceipt(chatID, readUpTo string) error { +func (mac *macOSDatabase) SendReadReceipt(chatID, readUpTo string, _ ...imessage.ExtraParams) error { return nil } -func (mac *macOSDatabase) SendTypingNotification(chatID string, typing bool) error { +func (mac *macOSDatabase) SendTypingNotification(chatID string, typing bool, _ ...imessage.ExtraParams) error { return nil } -func (mac *macOSDatabase) NotifyUpcomingMessage(eventID id.EventID) { +func (mac *macOSDatabase) NotifyUpcomingMessage(eventID id.EventID, _ ...imessage.ExtraParams) { } diff --git a/matrix.go b/matrix.go index 1d08af87..5eed6c2f 100644 --- a/matrix.go +++ b/matrix.go @@ -19,6 +19,7 @@ package main import ( "encoding/json" "fmt" + "strconv" "sync/atomic" "time" @@ -163,7 +164,7 @@ func (mx *WebsocketCommandHandler) handleWSStartDM(cmd appservice.WebsocketComma return false, fmt.Errorf("failed to parse request: %w", err) } req.ActuallyStart = cmd.Command == "start_dm" - resp, err := mx.StartChat(req) + resp, err := mx.StartChat(req, map[string]string{"ws_req_id": strconv.Itoa(cmd.ReqID)}) if err != nil { mx.log.Errorfln("Error in %s handler: %v", cmd.Command, err) return false, err @@ -202,18 +203,18 @@ func isNumber(number string) bool { return true } -func (mx *WebsocketCommandHandler) StartChat(req StartDMRequest) (*StartDMResponse, error) { +func (mx *WebsocketCommandHandler) StartChat(req StartDMRequest, traceData map[string]string) (*StartDMResponse, error) { var resp StartDMResponse var err error - if resp.GUID, err = mx.bridge.IM.ResolveIdentifier(req.Identifier); err != nil { + if resp.GUID, err = mx.bridge.IM.ResolveIdentifier(req.Identifier, imessage.ExtraParams{TraceData: traceData}); err != nil { return nil, fmt.Errorf("failed to resolve identifier: %w", err) } else if parsed := imessage.ParseIdentifier(resp.GUID); parsed.Service == "SMS" && !isNumber(parsed.LocalID) { return nil, fmt.Errorf("can't start SMS with non-numeric identifier") } else if portal := mx.bridge.GetPortalByGUID(resp.GUID); len(portal.MXID) > 0 || !req.ActuallyStart { resp.RoomID = portal.MXID return &resp, nil - } else if err = mx.bridge.IM.PrepareDM(resp.GUID); err != nil { + } else if err = mx.bridge.IM.PrepareDM(resp.GUID, imessage.ExtraParams{TraceData: traceData}); err != nil { return nil, fmt.Errorf("failed to prepare DM: %w", err) } else if err = portal.CreateMatrixRoom(nil, &req.ProfileOverride); err != nil { return nil, fmt.Errorf("failed to create Matrix room: %w", err) diff --git a/portal.go b/portal.go index 4eaa3595..dfc896d5 100644 --- a/portal.go +++ b/portal.go @@ -1168,7 +1168,13 @@ func (portal *Portal) HandleMatrixMessage(evt *event.Event) { msg.Body = "/me " + msg.Body } portal.addDedup(evt.ID, msg.Body) - resp, err = portal.bridge.IM.SendMessage(portal.getTargetGUID("text message", evt.ID, ""), msg.Body, messageReplyID, messageReplyPart, imessageRichLink, metadata) + resp, err = portal.bridge.IM.SendMessage( + portal.getTargetGUID("text message", evt.ID, ""), msg.Body, messageReplyID, messageReplyPart, imessageRichLink, metadata, + imessage.ExtraParams{TraceData: map[string]string{ + "event_id": evt.ID.String(), + "room_id": evt.ID.String(), + }}, + ) } else if len(msg.URL) > 0 || msg.File != nil { resp, err = portal.handleMatrixMedia(msg, evt, messageReplyID, messageReplyPart, metadata) } @@ -1265,7 +1271,13 @@ func (portal *Portal) handleMatrixMedia(msg *event.MessageEventContent, evt *eve } if !hasUsableThumbnail { portal.addDedup(evt.ID, caption) - return portal.bridge.IM.SendMessage(portal.getTargetGUID("media message (+viewer)", evt.ID, ""), caption, messageReplyID, messageReplyPart, nil, metadata) + return portal.bridge.IM.SendMessage( + portal.getTargetGUID("media message (+viewer)", evt.ID, ""), caption, messageReplyID, messageReplyPart, nil, metadata, + imessage.ExtraParams{TraceData: map[string]string{ + "event_id": evt.ID.String(), + "room_id": evt.ID.String(), + }}, + ) } } @@ -1309,7 +1321,13 @@ func (portal *Portal) handleMatrixMediaDirect(url id.ContentURI, file *event.Enc } } - resp, err = portal.bridge.IM.SendFile(portal.getTargetGUID("media message", evt.ID, ""), caption, filename, filePath, messageReplyID, messageReplyPart, mimeType, isVoiceMemo, metadata) + resp, err = portal.bridge.IM.SendFile( + portal.getTargetGUID("media message", evt.ID, ""), caption, filename, filePath, messageReplyID, messageReplyPart, mimeType, isVoiceMemo, metadata, + imessage.ExtraParams{TraceData: map[string]string{ + "event_id": evt.ID.String(), + "room_id": evt.ID.String(), + }}, + ) portal.bridge.IM.SendFileCleanup(dir) return }