トリガとインプットとアウトプット以外「データ操作」しか使ってないフロー (直近のチャネル投稿のうち自分がリアクションしてないものをリンク付きの一覧にしてチャットで送るフロー)

タイトル長い・・・のですが、表題のとおり、直近のチャネル投稿のうち自分がリアクションしてないものをリンク付きの一覧にしてチャットで送るフローを、トリガとインプットとアウトプット以外は「データ操作」のアクションだけで作りました。ループ処理なし!!速い!!

フローの全体像

むらさき~!そして全部0秒!

トリガとインプットとアウトプット

トリガ

ひとまず「手動でフローをトリガーします」にしています。
実運用ではスケジュール起動がいいかなと思います。

インプット

Teamsの「メッセージを取得します」で、対象にしたいチームとチャネルを指定します。
このアクションでは、指定したチャネルの直近20件の投稿の詳細情報を取得します。
★注意点★
・対象は親投稿のみ。返信は含まない。
・削除済みの投稿も含む・・・。
・アダプティブカードの投稿も含む。

アウトプット

「メッセージを取得します」で取得した20件を、以下のようにして、FlowBotさんからチャットで自分に送ります。
★絞り込み条件
・自分がリアクションをしていないもの
・削除されていないもの
★送信内容
・投稿日時、タイトル、投稿者の情報
・投稿に1クリックで飛べるハイパーリンク

「データ操作」していく!

①「選択」で必要な情報に絞る&扱いやすいように加工

「メッセージを取得します」で取得できる、メッセージごとの情報は詳細です。使わないものも多いので必要な内容に絞るのと、アウトプットとして欲しい内容にしていくために扱いやすいよう、加工しておきます。

また後で加工するので、マップの順番は気ににしなくてOK

投稿日時:メッセージが投稿された日時です。
「メッセージを取得します」のメッセージごとのデータの ”createdDateTime” が該当しますが、これは協定世界時での値なので、時差計算をした上で、見やすい形式に整形します。

formatDateTime(addHours(item()?['createdDateTime'],9),'yy/MM/dd HH:mm')

投稿者:投稿した人です。
「メッセージを取得します」のメッセージごとのデータの  ”from/user/displayName” がそのまま使えます。
FlowBotからの投稿の場合はnullになるようで、その場合は空になりますが、今日は気にしないことにします・・・。empty関数とif関数を使って、空の場合場合は「不明」にするとかしてもいいと思います。Incoming Webhookや、メールでの投稿だとどうなるのかしら。。。

item()?['from/user/displayName']

タイトル:投稿のタイトルです。
「メッセージを取得します」のメッセージごとのデータの ”subject” がそのまま使えます。
タイトルがない場合は空になりますが、今日は気にしないことにします・・・。empty関数とif関数を使って、空の場合場合は「なし」にするとかしてもいいと思います。

item()?['subject']

リンク:投稿へのリンクです。
「メッセージを取得します」のメッセージごとのデータの ”webUrl” がURLです。ハイパーリンクにしたいのですが、一旦、URLをそのまま持っておきます。

item()?['webUrl']

削除フラグ:該当のメッセージが削除されているかどうか?の情報です。
「メッセージを取得します」で取得できる直近20件の投稿には、すでに削除されたものも含みます。これはアウトプットでは除外したいので、このあとフィルタリングしやすいように加工して持っておきます。元のデータとしては「メッセージを取得します」のメッセージごとのデータの "deletedDateTime" を使います。メッセージを削除している場合にのみ存在するキーのようなので、この値がある=削除されていると判断することにして、empty関数を使います。
empty関数は引数の値が空かnullだとtrueを返すので、trueの場合=削除されていない、falseの場合=削除されている となります。

empty(item()?['deletedDateTime'])

リアクションフラグ:自分がリアクションしているかどうか?の情報です。
「メッセージを取得します」のメッセージごとのデータに、”reactions”があります。中身は1つ1つ(1人分ずつ)リアクションの情報の配列です。(以下は、1人がリアクションしているメッセージの”reactions”)

            "reactions": [
                {
                    "reactionType": "like",
                    "createdDateTime": "2022-12-14T15:18:54.243Z",
                    "user": {
                        "application": null,
                        "device": null,
                        "user": {
                            "id": "(リアクションした人のユーザーID)",
                            "displayName": null,
                            "userIdentityType": "aadUser"
                        }
                    }
                }
            ]

少々乱暴ですが、この情報のどこかに自分のUseIDが含まれていれば「自分がリアクションした」と判断することにして、このような式にしました。

contains(string(item()?['reactions']),triggerOutputs()?['headers']?['x-ms-user-id'])

string(item()?['reactions']) で、”reactions”の中身をまるっと文字列の値に変換しています。
triggerOutputs()?['headers']?['x-ms-user-id'] が、自分(フローをトリガした人)のUseIDです。動的なコンテンツには出てきませんが、トリガのアウトプットのJSONには含まれているのでこのように書けば使えます。
contains関数に2つの値を入れて、”reactions”の情報に自分のUseIDが含まれているかを判定します。contains関数では、第一引数(配列か文字列)に、第二引数が含まれる場合にtrueを返すので、
trueの場合:”reactions”に自分のUseIDが含まれている=リアクションしている
falseの場合:”reactions”に自分のUseIDが含まれていない=リアクションしていない
となります。

②アレイのフィルター処理で不要なデータを除外

アレイのフィルター処理を2回かけて、それぞれで以下の条件で絞り込みます。

・削除されていないもの
削除されていないものを残したいので、削除フラグが「true」という条件を指定。

・リアクションしているもの
リアクションしていないものを残したいので、リアクションフラグが「false」という条件を指定。

アクションを1つにして「詳細設定モード」で複合条件を指定してもいいのですが、ここはアクションを2つ重ねました。
重ねる場合は、2つ目の「アレイのフィルター処理」のインプットを、1つ目の「アレイのフィルター処理」のアウトプットにするのを忘れないように注意。

③「作成」で配列を並び替え

「作成」アクションで、配列の並び順を逆にするreverse関数を使って、並べ替えをします。
大本のインプットである「メッセージを取得します」で取得する直近20件の投稿は、新しいものから順に並んでいますが、通知するときは古い順がいいかなと思いました。好みの部分ではありますが、Teams上で投稿は古いもの→新しいものの順なので、それに合わせるイメージです。

ひっくり返すだけ。
reverse(body('アレイのフィルター処理_2'))

sort関数を使って投稿日時で並び替えてもいいのですが、その場合、このフローでいうと最初の「選択」アクションより前で並べ替えをする必要があります。 なぜなら、「選択」アクションで、投稿日時をformatDateTime関数で 'yy/MM/dd HH:mm' の形式にすることで、秒以下の部分を切り捨てているから・・・。このまま並び替えに使うと、投稿日時が近くて「分」までは同じものが意図した順になりません。
エクセルの書式設定なら、書式を変えても表示に使っていない部分の情報が消えるわけではないのですが、formatDateTimeでは消えてなくなるので注意が必要です。逆に「時間部分をカットして日付だけのデータする」といった目的にも使えます。(よく使う!)

④「選択」と「結合」で投稿するリスト(文字列)を作る

表示はこんな感じにしたいと考えました。

ここまでで作ってきたメッセージの情報(配列)を、このように1つずつ改行して記述した文字列にする必要があります。これ、以前の私だったら、迷わずApply to each の中で「文字列変数に追加」を使って作っていたと思います。
が・・!同じ結果を「選択」と「結合」のアクションでループなしで作れるんです。「データ操作」すごい!

まずは、「選択」でメッセージ1つ1つの情報を1行ずつにまとめた一時配列を作ります。

concat(
item()?['投稿日時'],
' ',
item()?['タイトル'],
'/',
item()?['投稿者'],
' <a href="',
item()?['リンク'],
'">LINK</a>'
)

マップの部分は、「テキストモード」に切り替えて、上記のように記載します。concat関数は引数の文字列を結合して1つの文字列にする関数です。
ここに、引数として、メッセージの投稿日時、全角スペース、メッセージのタイトル、全角スラッシュ、メッセージの投稿者、「 <a href="」という文字列、メッセージのURL、「">LINK</a>」という文字列 を設定します。
<a href="URL">LINK</a> は、HTMLのハイパーリンクのタグです。
これを実行するとこんな配列ができあがります。

マップの部分を「テキストモード」に切り替えて、ブラケットとか消すと、一次元配列になるというのが、私にとってはめちゃくちゃ画期的!

[
"22/12/14 23:26 TEST2/やまめ <a href="https://teams.microsoft.com/l/message/***">LINK</a>",
"22/12/14 23:29 TEST4/やまめ <a href="https://teams.microsoft.com/l/message/***">LINK</a>",
"22/12/14 23:29 TEST6/やまめ <a href="https://teams.microsoft.com/l/message/***">LINK</a>",
"22/12/14 23:29 TEST8/やまめ <a href="https://teams.microsoft.com/l/message/***">LINK</a>",
"22/12/14 23:29 TEST10/やまめ <a href="https://teams.microsoft.com/l/message/***">LINK</a>",
"22/12/14 23:29 TEST14/やまめ <a href="https://teams.microsoft.com/l/message/***">LINK</a>",
"22/12/14 23:29 TEST16/やまめ <a href="https://teams.microsoft.com/l/message/***">LINK</a>",
"22/12/14 23:29 TEST18/やまめ <a href="https://teams.microsoft.com/l/message/***">LINK</a>",
"22/12/14 23:29 TEST20/やまめ <a href="https://teams.microsoft.com/l/message/***">LINK</a>"
]

これを、「結合」アクションで、改行を入れながら1つの文字列にします。
チャットで送る際の形式はHTMLなので、改行を入れる=HTMLでの改行コードである<br>を入れるということになります。

シンプル~。

これで、「データ操作」は完了です!

チャットで送ってみる!

本文に、データ操作の最後のアクションのアウトプットを入れています。
投稿先の部分の表示がおかしくなってますが(^^;)、chat with flow botを選んでいます。

投稿先の部分が中国語になってる・・・。

元の投稿はTEST1~TEST20まで投稿しておき、
奇数番号にはリアクションをつけ、TEST12は投稿を削除してからフローを実行してみます。

あふれ出る自作自演感①
あふれ出る自作自演感②

ねらいどおり、削除したTEST12を除く、リアクションしていない偶数番号の投稿を、投稿の古い順にリストアップして通知できました。

リンクもちゃんと貼れてる。

実運用するなら・・・

  • 自分の投稿には普通リアクション付けないし、未読のチェックをする必要もないので、自分も投稿することがあるチャネルを対象とする場合は、投稿者が自分の場合を除いたほうがいいですね。

  • 手動起動ではなくスケジュール起動に。また、その場合は通知対象のメッセージがない場合の代替メッセージを指定しておく方がよさそう。(リアクションしてない直近の投稿はありません、など)


最後に①

途中でも書いてますが、「データ操作」を使えるようになる前に同じことを実現しようとしていたら、Apply to eachを入れ子で2段階使うようなフローを作っていたと思います・・・。
それを0秒で終わるフローにできるなんて!
これからも「データ操作」さんと、どんどん仲良くなりたいです。

最後に②

こんなフローが作れたのがうれしくて(笑)、Twitterでつぶやいたら、いつもブログやTwitterを拝見しているわたるふさん(ルドルフもわたるふもいろいろあってな)が「興味深い」なんて言ってくださり、気ままに勉強会でデータ操作を教えてくださった張本人のMiyakeさんもコメントくださって、調子に乗って記事にしてみました。 ありがとうございます!!!
そしたら、投稿日時で並び替えた方がいいな・・・とか、じゃなくてreverseでひっくり返す方がいいな・・・とか、また新たにいろいろ考えることができました。
アウトプットすると勉強になるっていうのはこういうことなのか・・・!と実感。
こちらも続けていきたいです!


この記事が気に入ったらサポートをしてみませんか?