BlueBubblesApp / bluebubbles-server

Server for forwarding iMessages to clients within the BlueBubbles App ecosystem
https://bluebubbles.app
Apache License 2.0
554 stars 47 forks source link

Can't Send Multipart Message with Attachment GUID #609

Closed trek-boldly-go closed 10 months ago

trek-boldly-go commented 10 months ago

I am building a bridge between BlueBubbles and Beeper so I am using the BlueBubbles REST API.

I am getting an error back from the SendMultipartMessage endpoint {{host}}/api/v1/message/multipart?password={{password}}

I upload the attachment using the endpoint {{host}}/api/v1/message/attachment?password={{password}} and get a response back like:

{
    "associatedMessageGuid": null,
    "associatedMessageType": null,
    "attachments": [
        {
            "guid": "BEA82CB2-09D2-4951-95F1-A7887C548C74",
            "hasLivePhoto": false,
            "height": 640,
            "hideAttachment": false,
            "isOutgoing": true,
            "isSticker": false,
            "metadata": {
                "height": 640,
                "size": null,
                "width": 640
            },
            "mimeType": "image/jpeg",
            "originalGuid": "BEA82CB2-09D2-4951-95F1-A7887C548C74",
            "originalROWID": 37,
            "totalBytes": 30334,
            "transferName": "Profile Photo.jpeg",
            "transferState": 5,
            "uti": "public.jpeg",
            "width": 640
        }
    ],
    "attributedBody": [
        {
            "runs": [
                {
                    "attributes": {
                        "__kIMFileTransferGUIDAttributeName": "BEA82CB2-09D2-4951-95F1-A7887C548C74",
                        "__kIMMessagePartAttributeName": 0
                    },
                    "range": [
                        0,
                        1
                    ]
                }
            ],
            "string": ""
        }
    ],
    "balloonBundleId": null,
    "cacheRoomnames": null,
    "chats": [
        {
            "chatIdentifier": "REDACTED",
            "displayName": "",
            "groupId": "0CB51309-B63A-49ED-8D04-AF53F879497F",
            "guid": "iMessage;-;REDACTED",
            "isArchived": false,
            "isFiltered": false,
            "originalROWID": 11,
            "properties": [
                {
                    "LSMD": "2024-01-18T21:39:35.070Z",
                    "hasBeenAutoSpamReported": true,
                    "hasViewedPotentialSpamChat": true,
                    "messageHandshakeState": 1,
                    "numberOfTimesRespondedtoThread": 3,
                    "pv": 0,
                    "shouldForceToSMS": false
                }
            ],
            "style": 45
        }
    ],
    "country": null,
    "dateCreated": 1705613975070,
    "dateDelivered": null,
    "dateEdited": null,
    "datePlayed": null,
    "dateRead": null,
    "dateRetracted": null,
    "didNotifyRecipient": false,
    "error": 0,
    "expressiveSendStyleId": null,
    "groupActionType": 0,
    "groupTitle": null,
    "guid": "5DD77C94-7392-4058-874A-C6D4E18CDFAA",
    "handle": {
        "address": "REDACTED",
        "country": "US",
        "originalROWID": 10,
        "service": "iMessage",
        "uncanonicalizedId": null
    },
    "handleId": 10,
    "hasDdResults": false,
    "hasPayloadData": false,
    "isArchived": false,
    "isAudioMessage": false,
    "isAutoReply": false,
    "isCorrupt": false,
    "isDelayed": false,
    "isExpired": false,
    "isForward": false,
    "isFromMe": true,
    "isServiceMessage": false,
    "isSpam": false,
    "isSystemMessage": false,
    "itemType": 0,
    "messageSummaryInfo": null,
    "originalROWID": 336,
    "otherHandle": 0,
    "partCount": 1,
    "payloadData": null,
    "replyToGuid": "56F814C5-7626-47D5-ADA9-4DBDDEAA631C",
    "shareDirection": 0,
    "shareStatus": 0,
    "subject": null,
    "text": "",
    "threadOriginatorGuid": null,
    "threadOriginatorPart": null,
    "timeExpressiveSendStyleId": null,
    "wasDeliveredQuietly": false
}

So I parse the response and basically get out response.Data.Attachments[0].GUID, which I use as the UUID in my multipart message request:

    request := SendMultipartMessageRequest{
        ChatGUID:            chatID,
        SelectedMessageGuid: replyTo,
        PartIndex:           &replyToPart,
        Parts: []MultipartMessagePart{
            {
                PartIndex: 0,
                Text:      text,
            },
            {
                PartIndex:  1,
                Attachment: attachmentId,
                Name:       attachmentName,
            },
        },
    }

I am only getting an error back from the endpoint:

{"data":{"handle":{}},"error":{"message":"Attachment 'BEA82CB2-09D2-4951-95F1-A7887C548C74' does not exist","type":"Validation Error"},"message":"You've made a bad request! Please check your request params \u0026 body","status":400}

But you can clearly see the id matches between the first response and the second response.

While digging through your code to figure out what I might be missing, I found that you have a loop to go over the parts of the request, but one of the lines inside the loop doesn't use the loop var i but instead a hardcoded 0:

https://github.com/BlueBubblesApp/bluebubbles-server/blob/4a36b7da39c8f71adaa8717f05a8b96516fad6cc/packages/server/src/server/api/interfaces/messageInterface.ts#L548

This probably isn't causing my issue which I still need help with, but I wanted to bring it to your attention.

Am I using the IDs incorrectly? Do I need to program in a wait time or something?

trek-boldly-go commented 10 months ago

Feel free to review my code yourself if you don't think the error is within BlueBubbles: https://github.com/mautrix/imessage/blob/b6b94a72a82d37468cd58db4058d254c76a5e220/imessage/bluebubbles/api.go#L618C1-L722C2

zlshames commented 10 months ago

I think this is a documentation issue (my fault) on how to send multipart messages. Actually, we don't even have it implemented into the BlueBubbles App yet.

Basically, the endpoint you used (/api/v1/attachment) was the endpoint to send an attachment. Just a single one, and it sends the attachment right away.

There is a different endpoint to use for multipart. The flow goes like this:

  1. Use the POST /api/v1/attachment/upload to upload all your attachments
    • this will return an ID for the attachment
  2. Use the POST /api/v1/message/multipart to send the uploaded attachments
    • use the IDs from the 1st requests

This should be the corresponding Postman Request: https://documenter.getpostman.com/view/765844/UV5RnfwM#d31dc599-5fe3-44d2-9872-cf64598a4fac

If you are just implementing sending attachments (1 at a time), you use this endpoint: https://documenter.getpostman.com/view/765844/UV5RnfwM#614127f4-d6af-41d7-998b-777747c641d7

trek-boldly-go commented 10 months ago

@zlshames That helps clear up quite a bit. I see the upload returns a hash only, is that what you call the UUID of the attachment?

tneotia commented 10 months ago

@trek-boldly-go yes that is the UUID that you use in the subsequent multipart request