きっかけ
自分が一番活用してるPowerAutomateの機能は受信メールのTeams通知です。
プロジェクト管理ツール(Redmine/Backlog等)のチケット登録/更新メールのTeams通知は対象者へメンション通知される、PC開かなくてもスマホTeamsで情報確認できるなどで、周りからも好評です(たぶん)。
ただ、メール受信数が増えると処理が滞留する(参考:PowerAutomate:繰り返し処理のパフォーマンス測定/最適化)など気をつける点もあり、受信するメールの種類でフローを分けることもあるので、テンプレになりそうな要素に絞って記事をまとめます。
事前準備
処理メールはトラブル回避をふまえ、少ないに越したことはないです。最低限に抑えるため、処理対象メールは事前にフォルダ振り分けし、PowerAutomateは該当フォルダへのメール受信をフックに処理させます。
Office365 Outlookにアクセスし、振分用フォルダを作成した後で「ホーム→…(三点メニュー)→ルール→ルールを管理」を選択します。
ポップアップ左上の「新しいルールを追加」を選択し以下設定を行い保存します。
1. ルールの名前
2. 条件(件名・送信元アドレスなどの条件を指定)
3. アクションを追加(「指定の場所に移動」で振分用フォルダを指定)
PCのOutlookで振り分け設定すると、Outlook起動時にメール振り分けされるため、PC未起動時は振り分けされません。必ず上記手順でサーバ側メール振分を行うようにして下さい。
フロー全体像
メールの種類でかき分けることをふまえ、共通テンプレになりそうな要素のみ記載してます。
①トリガー設定
フォルダアイコンから処理対象メールを受信するフォルダを選択します。
受信メール数が多すぎるとAPI連携閾値に引っかかり想定外のエラーが発生します。
事前準備を参考にフォルダ振り分けして処理対象メールは減らすことをお勧めします。
②変数初期化(+Teamsグループ/チャネルID確認)
変数定義を行います。
仮スコープ(3~8)はTeam ID/Teamsチャンネルを取得する処理なので、値が明らかであれば削除して問題ないです。
②-1~2、9~12) 変数初期化
『変数を初期化する』で以下6個の変数を設定します。
| 名前 | 種類 | 初期値 |
|---|---|---|
| TeamID | 文字列 | 投稿するTeamsID1 |
| TeamsチャンネルID | 文字列 | 投稿するTeamsチャンネルID1 |
| 件名 | 文字列 | @{triggerOutputs()?['body/subject']} |
| 内容 | 文字列 | (空値) |
| 本文行配列 | アレイ | (空値) |
| 締切時刻 | 文字列 | @{addMinutes(utcNow(),10)}2 |
②-3) TeamID未設定?
『条件』でTeamIDの空値判定をします。
- 左辺: (式)length(variables('TeamID'))
- 条件:次の値に等しい
- 右辺: (式)0
②-4) 参加済みチームをリストする
Teamsの『参加済みチームをリストする』で自身が参加するTeams情報を取得します。
本処理が実行されると以下インスタンス情報を配列化したJSONが出力されます。
[
{
"id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"displayName": "Teamsチーム名",
"description": "Teamsチーム説明",
"isArchived": false,
"tenantId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
},
:
]
投稿したいTeamの「id」の値を「②-1)TeamID変数を初期化する」に設定します。
②-5) 終了(TeamID未設定)
『終了』で状態=「取り消し済み」として処理を止めます。
②-6) チャンネルID未設定??
『条件』でTeamsチャンネルIDの空値判定をします。
- 左辺: (式)length(variables('TeamsチャンネルID'))
- 条件:次の値に等しい
- 右辺: (式)0
②-7) チャネルの一覧表示
Teamsの『チャネルの一覧表示』で指定したTeamID配下のチャンネル情報を取得します。
- チーム:
@variables('TeamID')
本処理が実行されると以下インスタンス情報を配列化したJSONが出力されます。
[
{
"id": "xx:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx@thread.tacv2",
"createdDateTime": "20YY-MM-DDTHH:mm:ss.xxxZ",
"displayName": "チャンネル名",
"description": "チャンネル説明",
"email": "msteams_xxxxx@xxxx.onmicrosoft.com",
"tenantId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
"webUrl": "https://teams.cloud.microsoft/l/channel/xxxxxxxxxxxxxxxxxxxx",
"membershipType": "standard",
"isArchived": false
},
:
]
投稿したいTeamの「id」の値を「②-2)TeamチャンネルID変数を初期化する」に設定します。
②-8) 終了(チャンネルID未設定)
『終了』で状態=「取り消し済み」として処理を止めます。
③スコープ1:本文抽出・加工
受信メール本文を改行区切りで配列化し、1行ごとにTeams投稿用の「内容変数」に書き込みします。記載するサンプルでは特別な加工は行ってないですが、必要に応じて読み飛ばしや文字変換、装飾など条件分岐を加えて加工します。
また、PowerAutomate:繰り返し処理のパフォーマンス測定/最適化 をふまえ、配列化ループ処理は「Do until」を使用し、処理スタック時のサーキットブレーカーも実装しています。
③-1) Html本文のテキスト化
Content Conversionの『Html からテキスト』で受信メール本文をHTMLからテキストに変換します。
- コンテンツ:
@{triggerOutputs()?['body/body']}
③-2) 全角SP除外文字列作成
『作成』で本文文字列の全角スペースを半角スペースに変換します。
- 入力:
@{replace(outputs('Html本文のテキスト化')?['body'],' ',' ')}
③-3) 本文行配列の処理行を抽出
『選択』で③-2)の値を改行コード3区切りの配列化し、空行と引用(先頭が> の行)を除去したものを抽出します。
- 差出人:
@split(replace(outputs('全角SP除外文字列作成'),decodeUriComponent('%0D'),''),decodeUriComponent('%0A')) - マップ(テキストモード):
@and(not(equals(length(item()), 0)),not(startsWith(item(), '> ')))
③-4) 本文行配列の設定
『変数の設定』で「本文行配列」に加工した配列地を設定します。
- 値:
@{body('本文行配列の処理行を抽出')}
③-5) 行数分繰り返し
『Do until』で条件に一致するまで繰り返し処理を行います。
- 左辺: (式)
length(variables('本文行配列')) - 条件:次の値に等しい
- 右辺: 0
③-6) 本文先頭行取り出し
『作成』で「本文行配列」の先頭行を抽出し、不要文字を除去するためtrim処理を行います。
- 入力:
@{trim(take(variables('本文行配列'),1)?[0])}
「変数の設定」で対象変数を直接加工するとエラーとなるため、仮変数で値を作成して代入処理しています。
③-7) 本文残り配列
『作成』で「本文行配列」の先頭行を除く、残りの行配列を変数作成します。
- 入力:
@{skip(variables('本文行配列'),1)}
③-8) 本文行配列再設定
『変数の設定』で「本文行配列」に本文残り配列を設定します。
- 値:
@{outputs('本文残り配列')}
「変数の設定」で対象変数を直接加工するとエラーとなるため、仮変数で値を作成して代入処理しています。
③-9) 条件(必要に応じて)
『条件』で読み飛ばしや加工条件を指定します。ここではテンプレなので常にTrueとなる条件を指定してます。
- 左辺: 1
- 条件:次の値に等しい
- 右辺: 1
③-10) 内容変数に追加
『文字列変数に追加』で「内容」に読取行文字列を追加します。
- 値:
@{outputs('本文先頭行取り出し')}
③-11) 締切時刻超過?
『条件』で現在時刻が締切時刻(初期化時の10分後)を超えているか判定します。
- 左辺: (式)
utcNow() - 条件:次の値より大きい
- 右辺: (式)
variables('締切時刻')
③-12) 本文行配列変数の設定(空値)
『変数の設定』で「本文行配列」を空配列に設定します。
- 値:
[]
③-13) 内容変数の設定(空値)
『変数の設定』で「内容」をNULL設定します。
- 値:
@null
③-14) 内容設定あり?
『条件』でTeams通知する「内容」変数が設定されてるか判定をします。
- 左辺: (式)length(variables('内容'))
- 条件:次の値より大きい
- 右辺: 0
③-15) 終了(本文なし)
「内容」変数が設定されてない場合、『終了』で状態=「取り消し済み」として処理を止めます。
④スコープ2:Teams通知(新規or返信)
④-1) メッセージを取得します
Teamsの『メッセージを取得します』で通知対象Teams/チャンネルのメッセージ一覧を取得します。
- チーム:
@variables('TeamsID') - チャネル:
@variables('TeamsチャンネルID')
④-2) 同一メッセージ件名の抽出
『選択』で通知対象Teams/チャンネルのメッセージのうち、同一件名のメッセージを取得します。
- 差出人:
@outputs('メッセージを取得します')?['body/value'] - 左辺: (式)
item()?['subject'], - 条件:次の値に等しい
- 右辺: (式)
variables('件名')
④-3) 同一メッセージあり?
『条件』で同一メッセージ件名が抽出できたか判定します
- 左辺: (式)
length(body('同一メッセージ件名の抽出')) - 条件:次の値より大きい
- 右辺: 0
④-4) チャネル内のメッセージで応答します
同一件名メッセージがあるので、Teamsの『チャネル内のメッセージで応答します』でTeams指定チャンネルの該当メッセージに応答投稿します。
- 投稿者:(選択)User
- 投稿先:(選択)チャネル
- メッセージID:
@{body('同一メッセージ件名の抽出')?[0]?['id']} - チーム:(カスタム値の入力)
@variables('TeamID') - チャネル:(カスタム値の入力)
@variables('TeamsチャンネルID') - メッセージ:
@{variables('内容')}
Teams指定チャンネル内に複数の同一件名メッセージがある場合、最初にヒットしたメッセージのみに応答します。
④-5) チャットまたはチャネルでメッセージを投稿する
同一件名メッセージがないので、Teamsの『チャットまたはチャネルでメッセージを投稿する』でTeams指定チャンネルに新規投稿します。
- 投稿者:(選択)User
- 投稿先:(選択)チャネル
- チーム:(カスタム値の入力)
@variables('TeamID') - チャネル:(カスタム値の入力)
@variables('TeamsチャンネルID') - メッセージ:
@{variables('内容')} - 件名:
@{variables('件名')}
Teams通知する「メッセージ」は必要に応じて固定分を加えるなど加工してください。
