2

In my question on getting text messages into Azure I was able to get the message of an SMS into Azure Table Store.

In the case where the text message contains images and thus has been sent as a MMS I want to download the images into Azure Blob Storage.

Twilo has a guide on how to download and receive images

But I am not sure how to make use of this with a Logic App

I tried studying this Twilio guide and cloned the code from GitHub. Confusingly the DownloadMmsImages is in the same solution as a BlockSpamCalls project. I was unclear about the relationship between the 2 projects.

I tried adding a Twilio GetMessage action however the only property that I am prompted to put in the Message SID is Body.

The (slightly obfuscated ) request body JSON schema is

{
  "body": {
    "$content": "VG9Db3VudHJ etc",
    "$content-type": "application/json",
    "$formdata": [
      {
        "key": "ToCountry",
        "value": "AU"
      },
      {
        "key": "ToState",
        "value": ""
      },
      {
        "key": "SmsMessageSid",
        "value": "SMeda21902 etc"
      },
      {
        "key": "NumMedia",
        "value": "0"
      },
      {
        "key": "ToCity",
        "value": ""
      },
      {
        "key": "FromZip",
        "value": ""
      },
      {
        "key": "SmsSid",
        "value": "SMeda2 etc"
      },
      {
        "key": "FromState",
        "value": ""
      },
      {
        "key": "SmsStatus",
        "value": "received"
      },
      {
        "key": "FromCity",
        "value": ""
      },
      {
        "key": "Body",
        "value": "Tskez7"
      },
      {
        "key": "FromCountry",
        "value": "AU"
      },
      {
        "key": "To",
        "value": "+61 etc"
      },
      {
        "key": "ToZip",
        "value": ""
      },
      {
        "key": "NumSegments",
        "value": "1"
      },
      {
        "key": "MessageSid",
        "value": "SMeda2 etc"
      },
      {
        "key": "AccountSid",
        "value": "AC7aa etc"
      },
      {
        "key": "From",
        "value": "+61 etc"
      },
      {
        "key": "ApiVersion",
        "value": "2010-04-01"
      }
    ]
  },
  "headers": {
    "Accept": "*/*",
    "Cache-Control": "max-age=259200",
    "Connection": "close",
    "Content-Length": "381",
    "Content-Type": "application/x-www-form-urlencoded",
    "Host": "etc.logic.azure.com:443",
    "User-Agent": "TwilioProxy/1.1",
    "X-Twilio-Signature": "sQf etc"
  }
}
Kirsten
  • 15,730
  • 41
  • 179
  • 318
  • Sorry, it took me three days to find time to write an answer... – Igor Soloydenko Dec 09 '17 at 07:09
  • https://learn.microsoft.com/en-us/azure/azure-functions/functions-compare-logic-apps-ms-flow-webjobs mentions "If a step in your integration scenario requires highly custom transformation or specialized code, then write a function and trigger the function as an action in your logic app." Looks Like I need to call a function as one of the steps – Kirsten Dec 10 '17 at 19:20
  • you don't _need_ it, but having it will simplify things a lot yes. Remember I said I'm planning to use Azure Logic Apps with Azure Functions. It's the workable way to deal with complex documents. – Igor Soloydenko Dec 10 '17 at 20:03
  • Are you sure it is possible to do it in Logic Apps alone? The MS Help sounds like it can't be done. – Kirsten Dec 10 '17 at 20:22
  • Yes, it's definitely possible, though not worth it. The MS Help doesn't say it can't be done. – Igor Soloydenko Dec 10 '17 at 20:42

1 Answers1

2

DISCLAIMER I apologize in advance if something in this answer does not quite work. I do not have an account with Twilio and am unable to test the solution end-to-end. Please, think of it as a draft that is supposed to demonstrate the main steps and key details required to achieve the desired results.

Getting the message SID

When you try to retrieve a message (or the message's media), you indeed need to provide the SID/SIDs of the message and/or the media, which are basically their identifiers. I assume, this instance of Logic App is receiving the SID(s) as a part of the incoming HttpRequest trigger.

For example, you may require the consumer of the Logic App to provide the message SID in the URL of an HTTP GET request, as shown below.

{
  "kind": "Http",
  "inputs": {
    "schema": {},
    "method": "GET",
    "relativePath": "message/{mms_message_sid}"
  }
}

HttpGet

Then, you will be able to refer to this SID in the further steps of your Logic App.

{
  "inputs": {
    "host": {
      "connection": {
        "name": "@parameters('$connections')['twilio']['connectionId']"
      }
    },
    "method": "get",
    "path": "/Messages/@{encodeURIComponent(triggerOutputs()['relativePathParameters']['mms_message_sid'])}.json",
    "authentication": "@parameters('$authentication')"
  }
}

UseSid

Retrieving the Message

I briefly looked at the Twilio connector and let me recommend you to not use it because it's going to complicate your Logic App a lot.

Instead, you can use the Get Media API via Http connector. You will need to construct a URL of the following form: https://api.twilio.com/2010-04-01/Accounts/{account_sid}/Messages/{mms_message_sid}/Media.json

retrive_media_list

You should then receive a response which contains the message's media_list with content_type and the uri you can use to download the actual content of the MMS.

{
  "first_page_uri":"/2010-04-01/Accounts/ACXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX/Messages/MM800f449d0399ed014aae2bcc0cc2f2ec/Media.json?PageSize=50&Page=0",
  "media_list":[
  {
    "sid":"ME85ebf7e12cb821f84b319340424dcb02",
    "account_sid":"ACXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
    "parent_sid":"MM800f449d0399ed014aae2bcc0cc2f2ec",
    "content_type":"image/png",
    "date_created":"Wed, 25 Sep 2013 22:47:18 +0000",
    "date_updated":"Wed, 25 Sep 2013 22:47:19 +0000",
    "uri":"/2010-04-01/Accounts/ACXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX/Messages/MM800f449d0399ed014aae2bcc0cc2f2ec/Media/ME85ebf7e12cb821f84b319340424dcb02.json"
  },
  {
    "sid":"ME8d8f717e2d6e5383055b3cd150ac5f54",
    "account_sid":"ACXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
    "parent_sid":"MM800f449d0399ed014aae2bcc0cc2f2ec",
    "content_type":"image/png",
    "date_created":"Wed, 25 Sep 2013 22:47:18 +0000",
    "date_updated":"Wed, 25 Sep 2013 22:47:19 +0000",
    "uri":"/2010-04-01/Accounts/ACXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX/Messages/MM800f449d0399ed014aae2bcc0cc2f2ec/Media/ME8d8f717e2d6e5383055b3cd150ac5f54.json"
  }
  ],
  "previous_page_uri":null,
  "uri":"/2010-04-01/Accounts/ACXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX/Messages/MM800f449d0399ed014aae2bcc0cc2f2ec/Media.json?PageSize=50&Page=0",
  "page_size":50,
  "next_page_uri":null,
  "page":0
}

Parse the response JSON

Since the response body is a JSON document expressed as a string, we won't be able to iterate media_list array, unless we jump through some hoops.

Just add a parse JSON step, which uses the body of the previous step (HTTP) as Content. For the schema provide the following document:

{
  "inputs": {
    "content": "@body('HTTP')",
    "schema": {
      "type": "object",
      "properties": {
        "first_page_uri": {
          "type": "string"
        },
        "media_list": {
          "type": "array",
          "items": {
            "type": "object",
            "properties": {
              "sid": {
                "type": "string"
              },
              "account_sid": {
                "type": "string"
              },
              "parent_sid": {
                "type": "string"
              },
              "content_type": {
                "type": "string"
              },
              "date_created": {
                "type": "string"
              },
              "date_updated": {
                "type": "string"
              },
              "uri": {
                "type": "string"
              }
            },
            "required": [
              "sid",
              "account_sid",
              "parent_sid",
              "content_type",
              "date_created",
              "date_updated",
              "uri"
            ]
          }
        },
        "previous_page_uri": {},
        "uri": {
          "type": "string"
        },
        "page_size": {
          "type": "number"
        },
        "next_page_uri": {},
        "page": {
          "type": "number"
        }
      }
    }
  }
}

parse_HTTP_response_body_json

Iterate through `media_list`

Now the things are relatively easier. You add a For Each step that targets media_list from the previous step.

For each media we need to make an API call that actually retrieves its binary contents. You may want to specify media type related HTTP headers. Play around that and see what works.

retrieve_media_content

Save into Azure Blob Storage

I will not provide any details here because you said, you already know how to do that for the SMS. Just make sure you add the necessary step(s) right after the HTTP2 (inside the For Each step).

Hope, it helps. Logic Apps is NOT "easy peasy integration squeeze".

Igor Soloydenko
  • 11,067
  • 11
  • 47
  • 90
  • Thanks Igor. What do you mean by "the consumer of the logic app?" When I try putting your suggestion for the relative path I get an error " Failed to save logic app mmsreceiver. The value 'message/{mms_message_sid)' provided in property 'inputs.relativePath' workflow trigger 'manual' of type 'Request' is not valid. The value must be a well formed URI relative path without any query parameters. Details: 'UriTemplate does not support '{mms_message_sid)' as a valid format for a segment or a query part.'." – Kirsten Dec 09 '17 at 18:08
  • I updated the question to show the request body JSON schema – Kirsten Dec 09 '17 at 18:18
  • I am just using the free Twilio trial – Kirsten Dec 09 '17 at 18:21
  • I just copied the SMS example that I got working. What is a manual trigger? – Kirsten Dec 09 '17 at 22:09
  • How about we use Chat? – Kirsten Dec 09 '17 at 22:25