2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

PowerAutomate:メール→Teams通知テンプレフロー

2
Posted at

きっかけ

自分が一番活用してる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通知する「メッセージ」は必要に応じて固定分を加えるなど加工してください。

  1. TeamsID/TeamsチャンネルIDが分からない場合は空値設定し、仮スコープ(②-3~8)を実装して確認してください。 2

  2. 処理スタック時のタイムアウトは10分としてます。必要に応じて変更してください。

  3. 改行コードは0x0dを除去し、0x0aを分離対象コードと扱っています

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?