PowerAutomateを使ってSharePoint上にある授業変更をTeamsに通知する授業変更BOTを作った話

## 背景
今年度から授業変更が学生会のSharePoint内のリストで公表されるようになり、既存の授業変更BOTが使えなくなったため作成した。

## システムの仕様
* 日付、時限、クラス、変更前科目、変更後科目
を要素として持つリストがSharePointに存在する。
* 平日の朝6時に**その日**の**自分のクラス**の授業変更情報を取得する。
* 授業変更が存在しているならばその内容をTeamsでで送信する。
* 授業変更が存在しないならばその旨をTeamsで送信する。
* (ex.変数を用いて改変しやすくする)

![Untitled Diagram (3).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1495553/7411d378-49e8-df12-b7ca-fbe6de04dcc8.png)


## PowerAutomate上でフローを作成
　今回、SharePoint上にデータがあるので、PowerAutomateを用いることで簡単にデータに触ることができる。
　PowerAutomateは、タイマーや特性の操作などをトリガーとして起動できる、自動化アプリケーションである。学生のOfficeアカウントで利用することができたので、今回はこれを利用することにしました。

1. PowerAutomate上で[作成]→[スケジュール済みクラウド フローを構築する]を押す。
1. 名前と初回の開始時間と繰り返し間隔を設定し[作成] を押す。
1. 作成したフローの編集画面が表示される。

## フロー上で用いる変数を定義する
　クラス名や情報を送信する対象のTeamsのIDなどを簡単に変更するために以下の方法で変数を定義します。
　今回は「チームID」「スレッドID」「対象の日付」「メッセージ本文」を格納するために4つ変数を定義します。また、変数名は自分が見て中身がわかるものであれば何でも構いません。
>
チームIDとスレッドIDはわざわざ変数に入れなくてもプルダウンで対象を選択できるのであえて作成する必要はないです。

1. [+新しいステップ]で出てきた検索窓に"変数"と入力。
1. アクション欄に出てきた選択肢から「変数を初期化する」を選択。
1. 「名前」に任意の変数名、「種類」はすべて[文字列]を選択。
1. 初期値を設定する。

### チームIDの初期値
　チームIDには、取得した授業変更情報の送信先チームの情報を入力します。
　必要な情報を得る手段は以下の通りです。

1. Teamsで送信先チームのページを開き、画面左ののチーム名が書いてある横の[...]をクリックする。
1. 出てきたメニューの中にある[チームへのリンクを取得]をクリックし、コピーする。
1. PowerAutomateのフローを作成しているページに戻り、コピーしたURLを「チームID」を初期化するブロック内の「値」の欄にペーストする。
1. ペーストした文字列の中から、「groupid=」とある場所を探し、これとこれより**左**の文字をすべて削除する。
1. 残った文字列の中から「&」とある場所を探し、これとこれより**右**の文字列をすべて削除する。
1. 残ったものがチームIDの初期値となる。

### スレッドIDの初期値
　スレッドIDには、取得した授業変更情報の送信先チーム内の送信先となるチャネルの情報を入力します。
　必要な情報を得る手段は以下の通りです。
1. ブラウザ上のTeamsで対象とするチームの対象とするスレッドを開く。
1. ブラウザのアドレス欄を確認する。
1. 「threadId=」とある場所を探し、それを含めずその右から「～@thread.skype」までを選択し、コピーする。
1. コピーした文字列を「スレッドID」を初期化するブロック内の「値」の欄にペーストする。

### 対象の日付の初期値
　対象の日付には、授業変更を取得する対象となる日付のタイムスタンプを格納します。
　今回は当日なので当日のタイムスタンプをそのまま格納しますが、翌日の授業変更を取得したくなった場合などにaddDays関数を用いることで簡単に対象の日付を変更することができます。

1. 「値」の欄をクリックし、右下に出てくる「動的なコンテンツ」のホップで、「式」をクリックする。
1. スクロールすると出てくる[日時]の中から、必要な関数をクリックして追加する。
    1. 今回はフローが実行される当日なので、utcNow()をクリックする（引数は不要）。


### メッセージ本文の初期値
　メッセージ本文には、送信するメッセージを文字列で格納します。
　今回は、メッセージの冒頭に何時の授業変更かを知らせる文章を格納します。

1. 上のように「式」を開く。
1. [日時]から、formatDateTime()関数を選択する。
1. ()の中にカーソルを動かし、「動的なコンテンツ」の中の[変数]から、「対象の日付が格納された変数」を選択する。
1. ```, 'yyyy年MM月dd日'```を入力
    1. これにより、受け渡したタイムスタンプを'yyyy年MM月dd日'にフォーマットした文字列を得ることができる。
1. 「OK」をクリックすると、[値]の欄にラベルのようなものが入る。
1. ```の授業変更```をラベルの後ろに入力する



## フロー上でSharePointリストのデータを取得する

1. [+新しいステップ]から「SharePoint」の「複数の項目の取得」を選択して追加。
1. 「複数の項目の取得」を左クリックで開き、目的のリストがあるSharePointを選択（またはURLをコピペ）し、リスト名から目的のリストを選択。
1. [詳細オプションを表示]をクリックし、「フィルター クエリ」の欄にODataフィルターを入力する。[ODataフィルタについて](https://docs.microsoft.com/ja-jp/azure/search/search-query-odata-filter)(自分でもまとめたい)

　今回は以下のフィルターを用いる。
　ただし、'〇-〇'は目的のリスト上でのクラス名と一致するものに書き換えること。また、「」で囲まれた二つの列の名前は、SharePoint上から持ってきた情報を「」ごと上書きすることで利用することができる。
列の名前を取得する方法は後に示す。

```
OData_「クラス名のある列の名前」 eq '〇-〇' and
OData_「授業変更の日付の列の名前」 eq '@{string(formatDateTime(utcNow(),'yyyy-MM-dd'))}'
```

### SharePointリストの列名の取得方法
　フィルタークエリに必要な列の名前は、以下の方法で取得することができる。


1. SharePoint上で目的のリストのページを開く。
1. 名前を取得したい列の要素を基準にして適当にフィルタをかける。
1. フィルタ後のページのアドレス欄から、「FilterField1=」となっている部分を見つける。
1. そのすぐ右から「＆」の手前までの間にある文字列が目的の列の名前なのでコピーする。

## 手に入れた情報をメッセージ本文の変数に格納する

　以上の作業で、SharePoint上のリストから当日の任意のクラスの授業変更情報を取得することができた。
　次は、手に入れた授業変更情報を1回にまとめて送信するためにメッセージ本文の変数にまとめるフローを作成する。

1. [+新しいステップ]をクリックし、「変数」の「文字列変数に追加」を選択し追加する。
1. [名前]欄ではメッセージ本文を格納した変数を選択する。
1. [値]欄には、送信したいメッセージの本文を入力する。「複数の項目の取得」で得た情報は[動的なコンテンツ]から選択することで挿入できる。

>
[動的なコンテンツ]から「複数の項目の取得」で得た情報を追加した際、「メッセージを～投稿する」のブロックが「Apply to each」というブロックの内に勝手に入ります。
これによって、「複数の項目の取得」で複数の行のデータが得られた際はそれぞれの行のデータで1回ずつ「Apply to each」内のフローを実行することができます。

　今回はこのような値にしました。

```
@{items('Apply_to_each')?['OData_時限のテーブル名/Value']}時間目　
　@{items('Apply_to_each')?['OData_変更前科目のテーブル名']}　→　@{items('Apply_to_each')?['OData_変更後科目のテーブル名']}



```


## Teamsで送信する
　以上の作業で、送信する文章のひな型を作成することができた。
　次はこれをTeamsで送信するフローを作成する。

1. [+新しいステップ]をクリックし、「Teams」の「メッセージをフロー ボットとしてチャンネルに投稿する（プレビュー）」を選択し追加する。
1. 「チーム」欄では「カスタム値の入力」を選択し、[動的なコンテンツ]から「チームID」を格納した変数を選択する。
1. 「チャネル」欄では「カスタム値の入力」を選択し、[動的なコンテンツ]から「スレッドID」を格納した変数を選択する。
1. 「メッセージ」欄には、送信したいメッセージの本文を入力する。[変数]の情報は[動的なコンテンツ]から選択することで挿入できる。



大体こんな感じになる

```
2025年12月25日の授業変更
5-8時間目　
　実験　→　数学
今日も一日お疲れ様でした。
また明日も頑張りましょう。
```

## 授業変更がない日のメッセージについて
　ここまでの作業で、授業変更を毎朝追加するBOTが完成した。しかし、今のままではこのシステムは以下の**太字**の仕様を満たしていない。


>
* **平日**の朝6時にその日の自分のクラスの授業変更情報を取得する。
* 授業変更が存在しているならばその内容をTeamsでで送信する。
* **授業変更が存在しないならばその旨をTeamsで送信する。**
*

　これらの問題を解決するため、作成したフロー内に条件分岐を組み込んでいく。


## 土日にフローを実行しない処理
　フローの一番上の「Recurrence」をクリックすると、フローの実行間隔が表示される。ここを見ても、平日のみトリガーさせるようなオプションは見当たらない。そこで、フローが実行された時に曜日を確認し、土日なら実行を止めるような分岐を追加する。これをもって平日の判定とする。

### 分岐の追加

1. フローとフローの隙間の追加したい場所にある＋をクリックする。
1. 「アクションの追加」をクリック[コントロール]の「条件」をさがして追加する。
1. はいの場合、いいえの場合が出てくる。この中にフローの処理を入れることができる。


## 土日に何もしない処理
　平日の判定として、土日に送信しない処理を作成する。これは祝日などには対応できない欠点があるが、簡単に実装できる上に効果が高い方法である。

1. 作成した分岐の中にある「値の選択」の中にある「式」を選ぶ
1. [日時]の中からdayOfWeek()関数を選択し、()の中に対象の日付の変数を入れる。```dayOfWeek(variables('対象の日付'))```のようになる
1. 真ん中は「次の値に等しい」を選択する
>
dayOfWeek()関数は、タイムスタンプを渡すとその日の曜日を数字で返す関数である。
日曜日が0
月曜日が1
火曜日が2
…
土曜日が6
を返す。

1. 土日を選択したいので、1つ目の右の欄は0を入力する
1. 「追加」を押して「または」で新しい条件を作る。
>
「and」と「または」がある。
またははOR条件のことであり、どちらかの条件に当てはまっていれば[はいの場合]が実行される。
andは、両方の条件に当てはまっていないと[いいえの場合]が実行される。

1. 2つ目の右の欄には6を入れる。
1. 今まで作ったフローの[複数の項目の取得]より下をすべて[いいえの場合]の中に入れる。
    1. これにより、[いいえの場合]の中に入れたフローは土日に実行されなくなる。


## 授業変更が存在しない日の処理
　今のままでは、「複数の項目の取得」の結果が0件の時（対象の日に授業変更がない日）に、「Apply to each」の中身が一度も実行され鵜に初期化時に設定した日時と投稿時に付け足した挨拶のみを送信することになってしまう。
　そこで、「複数の項目の取得」の結果が0件の時に別の文章を「メッセージ本文の変数」に追加することにする。

1. [条件]を[複数の項目の取得]の下に作成する。
1. 条件の左辺には、「式」内のlength()関数の中に「動的なコンテンツ」内の[複数の項目の取得]のアイテムの一覧を一つ選択して入れる。
    1. これにより、「複数の項目の取得」でいくつのデータが得られたかの数字を得ることができる。これが0の時、に分岐をしたい。
1. 真ん中は「次の値に等しい」右には「0」を入れる。
1. 「Apply to each」とその中身を[いいえの場合]に入れる。
1. 「はいの場合」の中に、[変数]の[文字列変数に追加]を作成し、授業変更がない旨を伝える文章を「メッセージ本文の変数」に追加する。（例：ありません。）



## EX:元は授業がないところに授業が入った時
　ここまでで、授業変更BOTが完成しました。強いて言うなら、授業がなくて変更前科目が空白になっているときに何も表示できないのがもどかしいですね。簡単に対策できるのでやってしまうといいでしょう。

1. 新しい変数として変更前科目を格納する変数（文字列型）を作成する
1. 「Apply to each」内の一番上で、「変更前科目の変数」に「複数の項目の取得」の「変更前科目のデータ」を格納する。
1. 既にある「文字列変数に追加」内にある、変更前科目の動的コンテンツをクリックし、以下のように書き換える。
1.  ```if(empty(variables('変更前科目')), 'なし', variables('変更前科目'))```
    1.  if()はいわゆる三項演算子である。3つ引数を取る関数で、1つ目の引数の値が真（True）の時、2つ目の引数の値を、1つ目の引数の値が偽（False）の時、3つ目の引数の値を返す関数である。
    1.  これにより変更前科目の中身が空文字の時、'なし'と入力することができるようになる。
    1.  variables('変更前科目')の「変更前科目」は自分の設定した変数名に書き換える必要がある

## EX2:slackなどほかのサービスで授業変更BOTを使いたいとき
　PowerAutomateにコネクタが存在するサービスなら、slackやOutlookなどを用いた授業変更の通知を行うこともできる。その場合は、既にあるteamsと同じ場所で同じ内容を送信するようにコネクタを作成すればいい。


## あとがき
　自分がTeamsで授業変更BOTを作成したときのことをまとめました。
　諸事情でTeamsが使えなくなったのでslackに送信先を変更するなどしましたがコネクタ一つ変えるだけで済んだので楽でした。
　この文章がお役に立てば幸いです。
　ご意見感想等お待ちしております。