MoreBeerMorePower

Power Platform中心だけど、ノーコード/ローコード系を書いてます。

チャットに投稿した画像を絵画風に加工し、テキストと一緒にツイートするLINE botを作ってみた

これまでもLINE botで受け取ったメッセージや画像を、Power Automate/Logic Appsを通じてTwitterに投稿するようなワークフローはいくつか作ってきましたが、今回は少し応用して「画像+テキスト」をLINE botで受け取って画像を加工しツイートするまでのフローを作ってみました。

まずはLINE上でのBotとのやりとりですが、下図のような流れになっています。

f:id:mofumofu_dance:20201107224103p:plain
LINE上でのBotとの会話

初めにユーザーがツイートに追加したい画像を投稿します。LINE bot はCloudmersiveで加工した結果の画像とOK/Cancelボタンを表示し、処理の続行を確認します。

続行の場合には、その後ツイートの本文を入力。これを受け取ったLINE botがツイートを行います。

実際に投稿されたツイートは以下のようになります。LINE botとやり取りした内容でツイートが構成されているのがわかります。

f:id:mofumofu_dance:20201107224222p:plain
投稿されたツイート

今回のポイントは、「LINE botでは画像とテキストそれぞれ1つずつしかイベントを受け取れない (画像とテキストを一緒に受け取ってイベント発生 にはできない) という点です。

Power Automate/Logic Apps でbotを作る場合には、なんらか、画像とテキストを結びつける必要があります。方法はいくつもありますが、ここではBlobストレージに一時的に画像を保存しておく手法をとりました。

以下ではフローの詳細を解説します。

Logic Appsでワークフローを作る

LINE bot ではHTTPアクションを多用するので今回はLogic Appsを選択しました。Previewで作っているので少し見た目が違いますが、アクションは共通なので適宜読み替えてください。

処理の概要は以下の通りです。正常なルート (画像確認でOKを押した場合) には基本的に、[分岐の一番左]→[分岐の真ん中]の順で処理されます。

f:id:mofumofu_dance:20201107231037p:plain
今回の処理概要

これをLogic Appsのワークフローとして作ると以下のようになります。作り方の問題で、一部上の概要とずれています。(Postbackイベントが一番下の「Condition」に入っている)

f:id:mofumofu_dance:20201107231538p:plain
Logic Appsのワークフロー全体

0. 受信したイベントのパース

後続のステップを楽に作るために (不要なループを発生させないために) 最初に発生したイベントの先頭をパースしておきます。

f:id:mofumofu_dance:20201107232146p:plain
イベントの0番目を対象に「JSONの解析」アクション

「JSONの解析」アクションを追加したら、first(triggerBody()?['events']) をコンテンツに追加します。JSONのスキーマには以下を設定しました。これで後続処理ではここでの解析結果を動的なコンテンツから選べるようになります。

{
    "message": {
        "properties": {
            "address": {
                "type": "string"
            },
            "fileName": {
                "type": "string"
            },
            "fileSize": {
                "type": "number"
            },
            "id": {
                "type": "string"
            },
            "latitude": {
                "type": "number"
            },
            "longitude": {
                "type": "number"
            },
            "packageId": {
                "type": "string"
            },
            "stickerId": {
                "type": "string"
            },
            "text": {
                "type": "string"
            },
            "title": {
                "type": "string"
            },
            "type": {
                "type": "string"
            }
        },
        "type": "object"
    },
    "postback": {
        "properties": {
            "data": {
                "type": "string"
            }
        },
        "type": "object"
    },
    "properties": {
        "replyToken": {
            "type": "string"
        },
        "source": {
            "properties": {
                "groupId": {
                    "type": "string"
                },
                "type": {
                    "type": "string"
                },
                "userId": {
                    "type": "string"
                }
            },
            "type": "object"
        },
        "timestamp": {
            "type": "number"
        },
        "type": {
            "type": "string"
        }
    },
    "type": "object"
}

1. 「メッセージ」イベントの処理

解析が終わった後、「メッセージ」イベントが発生した際の処理を入れます。

ここで処理の対象になるメッセージのタイプは「画像」と「テキスト」のみですので、これ以外の場合には例外としてエラーメッセージを送るようにします。

「スイッチ」アクションを追加して、JSON解析した結果からメッセージタイプを指定します。 body('parse_-_first_event')?['message']?['type']

f:id:mofumofu_dance:20201107233413p:plain
イベントのメッセージタイプで分岐

A. 「画像」タイプの場合

すべてのアクションを説明すると長くなりすぎるのでポイントだけ解説します。処理の全体は以下の通りです。

f:id:mofumofu_dance:20201107234515p:plain
画像メッセージの場合の処理

ポイント1. Blobストレージに保存するファイル名は [LINE userId].png

今回のワークフローでは受け取った画像を加工し、一時的にBlobストレージに保存しています。その際、Botが複数のユーザーとやり取りすることを考慮してファイル名を [LINEのuserId].pngにしています。

[LINEのuserId]はイベントの度に情報に含まれているので、前後の処理でBlobストレージを使った画像受け渡しに利用できます。

f:id:mofumofu_dance:20201108000709p:plain
ファイル名はuserIdを利用

ポイント2. 実行条件を工夫してエラーを抑止

画像の加工処理の前に、一度Blobストレージ上の保存済み画像を消す処理を入れています。念のために入れている処理なのですが、この次にくるアクションは「画像の削除が成功しようが失敗しようが続ける」としたかったので、実行条件を変更しています。

f:id:mofumofu_dance:20201108002727p:plain
画像加工処理の実行条件

そもそも画像の削除は不要かもしれません。その場合には実行条件の変更は不要です。

ポイント3. 確認用メッセージにはpostbackアクションをつける

画像の加工結果を投稿前に確認できるようにするメッセージには、処理の継続と中断を選択するボタンが用意されています。 このボタンのアクションに少し工夫をしています。

基本的にテキストタイプのメッセージはツイート本文だけにしたかったので、ボタンクリック時のアクションをpostback にしました。こうすることで、ユーザーの確認結果が「メッセージ」タイプのイベントと区別されます。

f:id:mofumofu_dance:20201108001536p:plain
確認メッセージの構成

ここで使っているFlex messageのJSONは以下の通りです。

{
  "messages": [
    {
      "altText": "this is a flex message",
      "contents": {
        "body": {
          "contents": [
            {
              "size": "md",
              "text": "この画像でよいですか?",
              "type": "text",
              "weight": "bold"
            },
            {
              "aspectMode": "fit",
              "margin": "none",
              "size": "5xl",
              "type": "image",
              "url": "@{outputs('Create_SAS_URI_by_path')?['body/WebUrl']}"
            }
          ],
          "layout": "vertical",
          "type": "box"
        },
        "footer": {
          "contents": [
            {
              "action": {
                "data": "keep",
                "displayText": "OK",
                "label": "OK",
                "type": "postback"
              },
              "height": "sm",
              "style": "primary",
              "type": "button"
            },
            {
              "action": {
                "data": "remove",
                "displayText": "Cancel",
                "label": "CANCEL",
                "type": "postback"
              },
              "height": "sm",
              "style": "secondary",
              "type": "button"
            }
          ],
          "flex": 0,
          "layout": "horizontal",
          "spacing": "sm",
          "type": "box"
        },
        "type": "bubble"
      },
      "type": "flex"
    }
  ],
  "to": "@{body('Parse_-_first_event')?['source']?['userId']}"
}

以上が「画像」メッセージの場合の処理概要とポイントでした。

B. 「テキスト」の場合

テキストの場合には画像よりも処理はシンプルです。

最初にBlobストレージ上に保存されているはずの画像 ([LINEのuserId].png) のコンテンツを取得します。

もしこれが失敗した場合、例えばユーザーが最初にテキストを入力したような場合、にはエラーメッセージを表示させています。

処理が成功した場合、つまりエラーメッセージの送信がスキップされた場合には、受信したテキストとBlobストレージから取得したコンテンツでツイートを実行します。

最後に、一連の処理の後片付けとして、Blobストレージの画像を削除しておきます。

下図で赤線になっている部分については実行条件が変更されている点に注意してください。

f:id:mofumofu_dance:20201108003405p:plain
テキストメッセージの場合の処理

これで「メッセージ」イベントの解説は終わります。

2. Postback イベントの処理

Postbackイベントは、画像の確認メッセージでボタンが押されたときにだけ発生します。

この時、メッセージタイプの分岐処理は必ず失敗するので、これを利用して スイッチアクションの後続に、スイッチが失敗したときだけ実行されるpostbackの処理を入れています。

f:id:mofumofu_dance:20201108004546p:plain
Postbackイベントの処理

Postbackイベントのハンドリングとして、条件分岐のアクションを入れています。 この分岐では、Postbackのデータ (body('Parse_-_first_event')?['postback']?['data']) が remove (=キャンセルボタン) かどうかを判定しています。

removeの場合には、保存されたBlobストレージの画像を削除し『処理をキャンセルしました』のメッセージを送っています。

remove以外の場合、処理の継続をするため本来は何もせず『続けてテキストを入れてください』のメッセージを送ってもよいのですが、念のため再度Blobストレージの画像の存在確認を行っています。

Postbackイベントの処理としては以上です。

おわり

以上解説してきたワークフローにより、1本のフローで異なるメッセージタイプ、イベントタイプの処理を行い、画像とテキストをツイートするLINE botを作ることができました。

今後の応用としては、画像の加工部分を顔にスタンプを押す処理に変えてみたり、あとは絵画風加工のテイストを複数作って、ユーザーに選ばせたりということが考えられます。

mofumofupower.hatenablog.com

もしここで紹介したフローが欲しい!という方がいらっしゃいましたらコメントを残してください。(もろもろ認証情報など消して)配布できるようにします。