Teamsの投稿からOneDriveに添付ファイルを一括ダウンロードするフロー

# はじめに（やりたいこと）
Teamsの投稿(メッセージ)で複数のファイルが送られてきたときに、添付されているすべてのファイルをダウンロードしたい。ということをPower Automate を使って実現しました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2572227/48718fdd-6e37-d787-9298-6e129639c3b7.png)

メッセージから選択してフローを起動すると、チャットで通知が届きます。
チャットに記載のリンクをクリックすると、自分のOneDrive for Businessに保存されたファイルが確認できる仕様です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2572227/605eb57e-94e9-ccf7-b39a-d12a15e53c32.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2572227/31e06909-2205-9063-90a7-66d5782bfbb9.png)


# フローの概要
フローの全体像がこちらです。
メッセージの詳細を確認して添付ファイルを確認し、Apply to eachで添付ファイルを１つずつ保存、最後に通知を送るという流れです。
![make.powerautomate.com_environments_Default-b794cd83-44cc-447c-856e-a27e55624c65_flows_shared_b2f9cde2-2e09-4483-91a6-fed5d7db87c0(PowerAutomate) (2).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2572227/0c2c1f54-b25b-8820-f8d0-61b602896635.png)


# フローの詳細
ここから、フローの要素それぞれについて解説していきます。

## 選択したメッセージの場合
Teamsのメッセージから手動で起動するためのインスタントトリガーです。
初期設定のままなにも編集していないので、今回特に解説することはありませんが、アダプティブカードを設定してフローの起動時にオプションを選択できるようにすると便利ですよね。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2572227/9385888b-c8c1-bd5a-2354-3b726820cfa4.png)

## メッセージ詳細を取得する
### メッセージの種類
トリガーのアウトプットには添付ファイルの情報が含まれていないので、改めてメッセージの詳細を取得します。
Teamsのメッセージには、チャネルの親投稿・返信、グループチャット、個人チャットと複数の種類がありますが、いずれかに関わらず[メッセージの種類]を[**グループチャット**]にすることで取得できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2572227/bc01b43b-ce2f-4005-08f3-8bdde7200d94.png)
詳しくは、わたるふさんの解説をご覧ください。

https://wataruf.hatenablog.com/entry/2022/04/02/145214

### メッセージへのリンク
メッセージの種類で分岐が必要ないのは利点ですが、グループチャットではなかった場合(チャネルの投稿の場合)には*MessageLink* が正しいリンクにならないようです。
最終通知に記載する、選択したメッセージへのリンクには、「メッセージ詳細を取得する」の*MessageLink* ではなく、**トリガー「選択したメッセージの場合」の*linkToMessage* を使用します。**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2572227/81fe91e2-fe1c-773e-2b6b-6585b32d5796.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2572227/eee5b3a2-9384-3338-a148-9a6e34bfc0fd.png)

通知のこの部分です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2572227/1fd4062f-6ec9-8a29-eaa6-c2b48a1a2ce8.png)


## 添付ファイルがなければ終了
取得したメッセージ詳細に対して、添付ファイルが１つもない場合は処理を終了します。
![make.powerautomate.com_environments_Default-b794cd83-44cc-447c-856e-a27e55624c65_flows_shared_b2f9cde2-2e09-4483-91a6-fed5d7db87c0(PowerAutomate).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2572227/9b0de051-32e0-e060-47c5-44842e651964.png)
条件には*empty* 関数を使用して、添付ファイルが空(なにもない)状態かどうかを確認し、いいえ：添付ファイルがなかった場合は[終了]アクションでフローを終了させます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2572227/326e0261-d639-79ed-703d-0a54fc740c45.png)


## 添付ファイルのダウンロード
添付ファイルがあった場合には*Apply to each* で添付ファイルを１つずつ保存していきます。

必須ではありませんが、時間短縮のためコンカレンシー制御はオンにしています。Teamsの投稿への添付ファイルは最大10件が登録できるようなので、並列処理の次数も合わせて10件にしています。(合わせる必要もないのですが、あってると気持ちがいいので😗)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2572227/702d7b9e-749b-e01e-24bb-c4bd33ac097a.png)

### そもそもTeamsの添付ファイルって？
以下の画像はチャネルの投稿へ添付ファイルを追加しようとする画面です。
4つの選択肢が出ていますが、実際はSharePointもしくはOneDrive for Businessに保存したファイルへのリンクが登録される仕様です。
[コンピューターからアップロード]からローカルのファイルを選択した場合でも、SharePointかOneDrive for Businessに保存されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2572227/8027a7a0-0d75-37d9-4b48-1d33ba6ec2b0.png)

そのため、「メッセージ詳細を取得する」で取得した添付ファイル(*attachments*)には、ファイルへのリンクURLが保存されています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2572227/238154b4-2ee3-8dbc-f28f-416bf63cb033.png)

:::note warn
一部例外があり、例えばアダプティブカードは*attachments* として保存されますが、*contentUrl* がnullであり、カードのJSONが*content* に保存されています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2572227/28a6f9a7-fc83-5475-06ee-9db45be71b2f.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2572227/a8a114b3-bd13-05ee-fec6-09c373e3877e.png)
:::

### パスによるファイル コンテンツの取得
おそらくココがこのフローの中で独自性のある部分です。
先ほど取得したファイルへのリンクURLである*contentUrl* から、ファイルのデータそのものであるファイルコンテンツを取得します。
*contentUrl* をうまく加工し、Inputに必要な[サイトのアドレス]と[ファイル パス]に分割します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2572227/d0eda4fd-e753-d566-4b01-6db321baac40.png)

ここで改めて*contentUrl* を確認すると、前半部分が[サイトのアドレス]、後半部分が[ファイル パス]になっていることがわかります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2572227/064029f7-379a-0bac-f0a7-5c47837454c0.png)

よって以下の関数で[サイトのアドレス]と[ファイル パス]を取得します。
```PowerFx:作成-split-
split(item()?['contentUrl'],'/')
```
```PowerFx:作成-dataset-
join(take(outputs('作成-split-'),5),'/')
```
```PowerFx:作成-path-
join(skip(outputs('作成-split-'),5),'/')
```

ここで一つ疑問が残ります。
SharePointのファイルはこのアクションで取得できるが、OneDrive for Businessから共有されたファイルはどうするのか？ということです。
**結論から言うと、OneDrive for BusinessのファイルもこのSharePointコネクタのアクションで取得することができます。**

OneDrive for BusinessのサイトURLを確認すると「sharepoint」という文字が含まれていることがわかります。
個人的な理解ですが、OneDrive for Businessは個人用のSharePointサイト といった感じで、本質的な中身は同質のように思います。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2572227/179cb5b8-1884-e29c-f030-fc5aab642542.png)

https://shanqiai.lekumo.biz/sharepoint_technical_note/2022/04/microsoft-teams-onedrive-for-business-sharepoint-filemanagement.html

### ファイルの作成
ファイルコンテンツが取得できたら、[ファイルの作成]で指定のフォルダにファイルを保存(ダウンロード)し、成功したファイル名として変数に記録します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2572227/2aa0fd17-5be3-7c73-04e5-c61c596ec961.png)

保存するフォルダ名はなんでもいいのですが、重複しないよう「タイムスタンプ＋メッセージのID」としています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2572227/5fd3e13f-6601-a61b-65ad-3c75fbcb34fb.png)

### 失敗時の処理
以上がダウンロード処理ですが、ダウンロード失敗に備えた処理も考えておきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2572227/df39e67f-d401-cb1a-5871-135c9faedeed.png)

考えられる失敗は様々ですが、例えば以下のようなことが考えられます。
- 投稿後にファイルが移動・削除されている。
- リンクが共有されたがダウンロードするための適切な権限がない。
- 共有されたリンクが間違っている。

スコープに続くアクションの設定、実行条件の構成から、失敗時のみに実行されるようにチェックを入れます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2572227/e6c70c4f-de16-fabd-6633-5eba5466b428.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2572227/e3850731-ca54-2e4b-591e-bc0c0425edcc.png)


https://qiita.com/kumukai/items/3010ee48079665c6ea23

## 結果の通知
最後にダウンロード結果を通知します。
![make.powerautomate.com_environments_Default-b794cd83-44cc-447c-856e-a27e55624c65_flows_shared_b2f9cde2-2e09-4483-91a6-fed5d7db87c0_backUrl=%2Fenvironments%2FDefault-b794cd83-44cc-447c-856e-a27e55624c65%2Fflows%2Fsh.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2572227/ba343327-38b7-d986-c04a-7ac0fe81945c.png)

### ダウンロードフォルダへのリンク
OneDrive for Bussinessコネクタには、ファイルやフォルダへのリンクを返すアクションがありません。
共有リンクを作成する方法もありますが、下手に他の人が見られる状態にはしたくないですし、なによりフォルダへのリンクはこの方法ではできません。

https://wataruf.hatenablog.com/entry/2022/05/08/002634

なので、無理やりな感はありますがリンクURLを自力で生成しています。

**https**から始まり、**${テナント固有の文字列}-my.sharepoint.com/personal/**　に続き、ユーザー固有のページを特定する情報として、**UPN**(ユーザープリンシパルネーム)から決まるアドレスが記載されます。
UPNのうち、URLとして使用できない「@」「.」が「_」に置換されていたので、同じように*replace*で置換しています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2572227/db426d03-a62a-7409-8c21-42d62bae8318.png)
```PowerFx:作成
replace(
    replace(
        outputs('マイ_プロフィールの取得_(V2)')?['body/userPrincipalName'],
        '@',
        '_'
    ),
    '.',
    '_'
)
```
```PowerFx:文字列変数に追加-結果-（URL部分）
https://v45dm-my.sharepoint.com/personal/@{outputs('作成')}/_layouts/15/onedrive.aspx?id=/personal/@{outputs('作成')}/Documents@{variables('フォルダ')}
```
ただし、こちらは開発用の自テナントのみでの検証結果ですので、フォルダへのリンクはあくまで**おまけ**として扱っていただき、リンクが機能しない場合には、通知が届いたらOneDrive for Businessのホームから直接ファイルを確認いただくようお願いします。

# おわりに
参考にフローのエクスポートファイルをgithubに保存しています。
ダウンロードフォルダへのリンクのテナント固有の文字列を各自の環境に合わせて書き換えていただく必要があるので、その点はご注意ください。

https://github.com/ksgiksg/Power-Automate/blob/main/%F0%9F%91%86%E6%B7%BB%E4%BB%98%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%81%AE%E4%B8%80%E6%8B%AC%E4%BF%9D%E5%AD%98_20230527074903.zip