見出し画像

メンバーのフルネームを指定してTeamsでメンション付き投稿とか個別チャットとかする

私の勤務先の組織では、社内のファイルサーバーにあるデータベースにメンバーの名前やメールアドレスが登録してあり、そのデータベースを元にメンバーのリストを使って何らかの一覧や管理表を作ることが多々あります。その一覧に、各自がアンケートの回答や作業状況を入れてね、ということも多く、期日までに対応していない人に、早く~!と催促する方法は、かつてはメールが主流でしたが、Teamsを導入して数年が経過しメールではなくTeamsの投稿やチャットでするということが増えてきました。
しかし、名前のリストを見ながら、Teams上で該当するメンバーへのメンションを1人ずつしたり、1人ずつ名前を検索してチャットに書き込んだりするのって面倒ですよね。(2,3人ならいいけども。)
そこで、メンバーのフルネームのリストをエクセルやテキストからコピペで記入し、その人たちにメンションしてチャネル投稿したり、個別のチャットに同じ内容をメッセージ送信したりするアプリをPower Apps+Power Automateで作ってみました。

大前提

ご紹介する方法は、対象とする組織(チーム)に所属するメンバーのフルネームが重複していないことが大前提になります。
同じ名前の人がいたら、フルネームだけの情報ではどのユーザーか特定できないので・・・。

前準備

メンバーのフルネームと、Teams上でメンションなどに使用するユーザーID(~~@***.microsoft.com)を紐付ける必要があるので、メンバーの情報をSharePointリストにしておきます。
このリストはもちろんPower Automateで作ります。
①Office 365 groups の「グループ メンバーの一覧表示」アクションで、チームを指定してメンバーの一覧を取得。
②事前に作っておいたSharepointリストに①の内容を書き込む。
 この時、メンバーの姓と名をスペースなしで結合して入れておくフィールドを作っておきます。(画像の、FirstNameLastName の部分)

通常、チームのメンバーは増減するので、最初にリストを作成した後は、定期的にこのリストを更新する(上記②の代わりに元のリストに同じ人の項目がある場合はその項目を更新する、ない場合は追加、逆にリストに載っているけどいなくなった人は削除するという仕組みにする)フローを作っておくといいと思います。

メンバー情報のSharepointリスト

Apps(チャネル投稿版)

詳細は省略しますが用意するのは以下。
※Appsの方を先に書いていますが、作成するときは後述のAutomateのフローを先に作り、Appsからはそのフローに接続する処理をします。
≪インプット≫
①チームを選ぶドロップダウン(使用者が所属しているチームから選べる)
②チーム内のチャネルを選ぶドロップダウン(①のチーム内に存在するチャネルから選べる)
③メンションしたいメンバーのフルネームを書く複数行可のテキストボックス(メンバーのフルネームを改行しながら書く)
④メッセージの内容を書く複数行可のテキストボックス
≪ボタン≫
「送信」ボタン
送信ボタンで、後述のPower Automateのフローを起動し、①~⑤の内容と使用者のユーザーIDをフローのインプットとして与えます。
このとき、①と②は選択した項目のIDの値、③は含まれる改行コードを「;」に置換した文字列、④は含まれる改行コードを「<br>」に置換した文字列にしておきます。

Automate(チャネル投稿版)

トリガーはPowerApps(V2)で、入力としてAppsから入力される5つの値を設定します。すべて文字列です。

まずはキモ中のキモ、③メンションしたいメンバーのフルネームから、1人1人のメンションコードを連ねた文字列を作る部分です。

まず、③の文字列を1人分ずつに分割して配列に格納します。
Appsからの入力時に、改行コードを「;」に変換したので、この時点での入力は「名前1;名前2;名前3」のようになっています。
これを、split関数で配列に変換します。
使うアクションは「データ操作」の「作成」。

split(triggerBody()['text_2'],';')

triggerBody()['text_2'] の部分が、トリガーの「Members」です。動的な値から選ぶと、式ではこうなりました。これを「;」で区切って、配列にするという式です。

が、ここでもうひと工夫。
フルネームとして入力される名前には、姓と名の間にスペースが入っていることがあります。あと、うちの組織のTeams上の表示名はデフォルトで、姓+名+(会社名)になっているので、こっちの羅列を入力されるかもしれません。このままだと、SharePointリストに作成した姓+名のデータとマッチしないので、邪魔な文字列を取り除く必要があります。
入力時に除いてね!ということもできますが、このくらいはシステム側でなんとかしてあげようという優しさの部分です。(このくらい普通かもしれませんが・・・。)
というわけで、さきほどの式をこのように変えます。

split(replace(replace(replace(triggerBody()['text_2'],'(会社名)',''),' ',''),' ',''),';')

(会社名)、半角スペース、全角スペースを順にreplace関数で空白に置換しました。
これで、「名 前1;名 前2;名前3(会社名)」のような文字列が以下のような配列になります。

[
  "名前1",
  "名前2",
  "名前3"
]

この配列の1つ1つを、SharePointリストから参照して、TeamsIDを取ってきたいわけですが、Appsで入力時の手間を省くため、「1人1行で改行しながら」+「空白行はOK」としているので、配列に空白のデータが含まれる場合があります。このあと1要素ずつループするのですが、余計なデータははなから除いておきたいので、空白を除外した配列を作り直します。
使うアクションは、「データ操作」の「アレイのフィルター処理」です。
「作成」の出力結果をインプットにして、以下の条件を設定します。
item()
次の値に等しくない
空白 ※式の入力で「’’」

「作成」と「アレイのフィルター処理」

これで、Appsで

名 前1
名 前2
  (←全角スペース)
   (←半角スペース)
  (←空白)
名前3(会社名)

のように入力され、フローに

名 前1;名 前2; ; ;;名前3(会社名)

のように取り込まれた文字列が、

[
  "名前1",
  "名前2",
  "",
  "",
  "",
  "名前3"
]

こうなって、

[
  "名前1",
  "名前2",
  "名前3"
]

こうなります。

この配列のデータを1つ1つ処理していきますが、先にSharePointリストを取得しておきます。
使うアクションは「SharePoint」の「複数の項目の取得」。
事前に用意したおいたメンバー情報のSharepointリストを取得します。
配列のデータとこのリストを使って、以下の処理をします。
あまり使いたくないけど、ここはApply to eachさんを使うしかあるまい。(多分)

  1. 配列のデータ(フルネーム)に該当するレコードを参照

  2. そのレコードのユーザーIDの値を取得

  3. ユーザーIDからメンションコードを取得

  4. 文字列変数に追加

配列のデータ(フルネーム)に該当するレコードを参照 には、「アレイのフィルター処理」を使いました。
リストの「FirstNameLastName」フィールドの値が、配列の値(items('Apply_to_each'))と一致するレコードに絞り込みます。

「アレイのフィルター処理」の条件の部分は詳細モードだとこうなります。

@equals(item()?['FirstNameLastName'], items('Apply_to_each'))

そのレコードのユーザーIDの値を取得 「作成」アクションで、1件目のレコードのユーザーID(リストのフィールド名は「Email」)の値を取得します。

first(body('アレイのフィルター処理_2'))?['Email']

「アレイのフィルター処理」で絞り込んだリストは1件になっているはずですが、first関数で1件目のレコードを指定しないと、ここでまたApply to eachが自然発生してしまいます。
※この後のアクションに、この式を直接入れてもいいのですが、途中の状態がわかりやすいように「作成」を挟みました。

こうして取得したユーザーIDをインプットにして、
・Teamsのアクション「ユーザーの @mention トークンを取得する」でメンショントークンを取得
・取得したメンショントークンを「文字列変数に追加」
文字列配列にはメンショントークン+「さん」+全角スペースを1つ入れました。
※事前に、フローの頭で「変数を初期化する」で文字列変数を用意しておきます。

これでループは終了。あとは投稿するだけ!
Teamsの「チャットまたはチャネルでメッセージを投稿する」アクションで、以下のように設定します。
投稿者:ユーザー
投稿先:チャネル
チーム:Appsから入力したチームのID
チャネル:Appsから入力したチャネルのID
メッセージ:Apply to eachで作成したメンション文字列と、Appsから入力したメッセージ

実行してみると・・・
メンション付きで投稿できました!

補足

説明したフローでは、以下の処理を省略していますが、実運用する場合はあった方がいいと思います。

投稿の件名
Appsの入力で、メッセージの本文とは別に1行文字列で「件名」を用意し、「チャットまたはチャネルでメッセージを投稿する」アクションの件名に設定する

メンションするメンバーが未指定の場合の処理
メンションしたいメンバーの配列に対しApply to eachをする前に、配列が空出ないかをチェックして、空の場合はApply to eachの処理をスキップする

メンションするメンバーのメンショントークンが取得できない場合の処理
メンションしたいメンバーの配列に対するApply to eachで、SharePointリストからの絞り込み結果が0件の場合(SharePointリストに該当するレコードが見つからなかった場合)は、ループ内の以降の処理の代わりに、エラーになった人の名前として別の文字列変数に格納する。そして、ループ終了後に、「エラーになった人の名前」の文字列が空白でない場合は、投稿する代わりに、使用者に対して、「この名前の人がエラーになったので、修正してやり直してね」と通知する。
※メンションできなかった人のみを除外して投稿する手もありますが、その場合、結局投稿し直したり、投稿済みの内容を修正したりすることになるので、投稿しないで通知してあげる方がいいと思います。

投稿前の確認のステップ
これは本当にオプションなのですが、実際に作ったフローでは、投稿をする前に使用者に対し、アダプティブカードを送付して「この内容で投稿するよ。いい?」と確認するステップを入れました。
Appsに表示するのがスマートな気もしますが、その場合、AppsとAutomateフローの関係性が複雑になるので、シンプルに済ませるためにアダプティブカードを使っています。

個別チャットの場合

メンションしてチャネル投稿ではなく、個別チャットにメッセージを送信する場合は、Appsからの入力はメンバーのフルネームの文字列と、メッセージの本文の内容の2つだけ。
Automateでは、メンバーのフルネームの文字列を空白を除外して配列にしたあと、ループの中でSharePointリストから該当メンバーのEmailの情報を取得するところまでは同じで、その後はそのEmailをインプットにして、
①Teamsの「チャットを作成」
②「チャットまたはチャネルでメッセージを投稿する」で投稿先をグループチャット、グループチャットは①で作成したチャットの会話IDをインプットにする
でOKです。

最後に

いろいろはしょりながらも、だらだらと書いてしまいましたが・・・
私は本格的にPower Platformを使い始めて2年ほどです。急に在宅勤務が始まりTeamsを初導入したのが2020年の夏。上司からAppsの作成方法が書かれたブログ記事とともに、アプリの作成を指示されて初めてPower Platformの存在を知り、AppsやAutomateをさわり始めました。そこから、エキスパートな方たちのブログを拝見したり、コミュニティーに参加して勉強したりして今に至ります。成長スピードが速いのか遅いのかはわかりませんが、「Apps?なにそれ?」と思いながら始めた最初の頃に比べたら、ものすごくいろいろなことができるようになりました。
特に、気ままに勉強会(https://kimamani.connpass.com/)では毎回ヒントをたくさんいただいています。本当にいつもありがとうございます!
初学者の場合「何ができるのかわからない」という状態なので、何かを調べる場合は「今やりたいこと」の中で「わからないことを調べる」ことをググって調べる・・・というのが基本だと思うのですが、こういった勉強会に参加すると「こんなこともできる」ということに気付くことができ、それがとても有効だと感じています。
これからもわくわくしながらスキルを磨き、アウトプットもして、私も誰かの役に立てるといいな~と思っています。


いいなと思ったら応援しよう!