Teamsのチャネルメッセージを完璧にエクスポートしたい
Teamsのチームは用途が終わったら削除する前提で設定されていますが、削除前に会話のエクスポートが出来ません。なのでこんな感じで困ります。
- 情報システム部「もうそのチーム使い終わったでしょ!成果物のファイルを整理して、チーム削除して」
- チームオーナー「削除してもいいけど、会話は後で参照したいから残しといて」
- 情報システム部「ぐぬぬ」
よっていつまで経ってもゴミチームが残ったままです。
Teamsのチャネルメッセージをエクスポートする方法
1. セキュリティ/コンプライアンスからエクスポート
セキュリティ/コンプライアンスを使えば、Teamsのチャネルメッセージをエクスポートできます。しかしながら
- チャネル毎にエクスポートできない
- 親投稿や返信の関係性をうまくエクスポートできない
という問題があります。あくまで監査用であり、ユーザーが見やすい形でエクスポートはできないようです。
2. GraphAPIを使ってエクスポート
APIを使えばチャネル毎に親投稿や返信の関係性も含めてエクスポートできます。
ということで、2でやります。
技術的な話
APIはこちら。
- チャネル メッセージを一覧表示する – Microsoft Graph beta | Microsoft Docs
- チャネルメッセージの返信を一覧表示する – Microsoft Graph beta | Microsoft Docs
こんな感じで取得します。
- チーム特定(ID取得)
- GET /groups/
- チーム/チャネル特定(ID取得)
- GET /teams/{id}/channels/
- 指定したチャネルの親投稿一覧取得
- GET /teams/{id}/channels/{id}/messages/
- 指定した親投稿への返信取得
- GET /teams/{id}/channels/{id}/messages/{id}/replies
他の事例はこちら。
- Microsoft Teams Scraping – Qiita
- Microsoft Teamsの特定チャネルのいいねをした人ランキングを作成しよう!その1~Azure ADにアプリを登録する~ │ システム運用日記
てことで、取るだけなら割と簡単です。
完璧なエクスポート
ここからが本題です。Teamsのチャネルメッセージにはさまざまな形式で投稿できるため、完全な再現を目指すとそれらを全て取得する必要があります。考慮すべきメッセージ種別を一覧にしてみました。
- ユーザーの投稿
- メッセージ属性
- 投稿者
- 投稿日時
- 編集日時
- メッセージヘッド
- 投稿/アナウンス
- アナウンスヘッド
- アナウンス背景画像
- サブヘッド(件名)
- メッセージ重要度(重要/普通)
- メッセージ本文欄
- メンション
- 個人メンション
- チャネルメンション
- チームメンション
- メッセージ本文
- 日本語
- 日本語以外
- 絵文字
- Giphy
- ステッカー
- HTML装飾
- 太字/斜体/下線/取り消し線
- ハイライト/フォントの色/フォントサイズ
- 段落(見出し1/見出し2/見出し3/段落/等幅
- インデント/箇条書き/段落番号
- 引用/リンク/コードスニペット
- 段落罫線/表
- 画像挿入
- 画像
- gif画像(そもそも挿入可能?)
- 添付ファイル
- リアクション(いいね)
- いいね/ハート/笑い/びっくり/悲しい/怒っている
- リアクション者
- メンション
- メッセージ属性
- ユーザー以外の投稿
- botの投稿
- コネクタ
- IncomingWebhook
- メール投稿
うーん、めんどくさい。
単純な取得
ひとまず少しやってみましょう。
こいつをエクスポートしたのがこちらです。ちなみに以下のAPIをGETで叩いてます。
https://graph.microsoft.com/beta/teams/738f192b-de2d-4cb2-bda8-0273cc7299ba/channels/19:51b38c312f234daa9bb8e9d05aafd796@thread.skype/messages/1565397760720/replies
{
“@odata.context": “https://graph.microsoft.com/beta/$metadata#teams('738f192b-de2d-4cb2-bda8-0273cc7299ba’)/channels('19%3A51b38c312f234daa9bb8e9d05aafd796%40thread.skype’)/messages('1565397760720’)/replies",
“@odata.count": 5,
“value": [
{
“id": “1565398720134",
“replyToId": “1565397760720",
“etag": “1565436221717",
“messageType": “message",
“createdDateTime": “2019-08-10T00:58:40.134Z",
“lastModifiedDateTime": “2019-08-10T11:23:41.717Z",
“deletedDateTime": null,
“subject": “",
“summary": null,
“importance": “normal",
“locale": “en-us",
“webUrl": “https://teams.microsoft.com/l/message/19%3A51b38c312f234daa9bb8e9d05aafd796%40thread.skype/1565398720134?groupId=738f192b-de2d-4cb2-bda8-0273cc7299ba&tenantId=1ae8725b-e1ff-45f3-b4ff-fb21ab4dc17f&createdTime=1565398720134&parentMessageId=1565397760720",
“policyViolation": null,
“from": {
“application": null,
“device": null,
“conversation": null,
“user": {
“id": “72c2cf01-fc7b-4b30-8020-798e87831dcc",
“displayName": “Administrator MOD",
“userIdentityType": “aadUser"
}
},
“body": {
“contentType": “html",
“content": “<div><div>\n<div>\n\n<div itemprop=\"copy-paste-block\">\n\n<div style=\"font-size:14px\">\n\n<div style=\"font-size:14px\">10_convでの返信です。いいねなどを付与します<span class=\"animated-emoticon-20-smile\" title=\"スマイル\" type=\"(smile)\"><img itemid=\"smile\" itemscope=\"\" itemtype=\"http://schema.skype.com/Emoji\" src=\"https://statics.teams.microsoft.com/evergreen-assets/skype/v2/smile/20.png\" alt=\"😄\" style=\"width:20px; height:20px\"></span></div>\n</div>\n</div>\n</div>\n</div>\n</div>"
},
“attachments": [],
“mentions": [],
“reactions": [
{
“reactionType": “heart",
“createdDateTime": “2019-08-10T00:58:44.29Z",
“user": {
“application": null,
“device": null,
“conversation": null,
“user": {
“id": “72c2cf01-fc7b-4b30-8020-798e87831dcc",
“displayName": null,
“userIdentityType": “aadUser"
}
}
}
]
},
{
“id": “1565398694352",
“replyToId": “1565397760720",
“etag": “1565398707790",
“messageType": “message",
“createdDateTime": “2019-08-10T00:58:14.352Z",
“lastModifiedDateTime": “2019-08-10T00:58:27.79Z",
“deletedDateTime": null,
“subject": null,
“summary": null,
“importance": “normal",
“locale": “en-us",
“webUrl": “https://teams.microsoft.com/l/message/19%3A51b38c312f234daa9bb8e9d05aafd796%40thread.skype/1565398694352?groupId=738f192b-de2d-4cb2-bda8-0273cc7299ba&tenantId=1ae8725b-e1ff-45f3-b4ff-fb21ab4dc17f&createdTime=1565398694352&parentMessageId=1565397760720",
“policyViolation": null,
“from": {
“application": null,
“device": null,
“conversation": null,
“user": {
“id": “72c2cf01-fc7b-4b30-8020-798e87831dcc",
“displayName": “Administrator MOD",
“userIdentityType": “aadUser"
}
},
“body": {
“contentType": “html",
“content": “<div><div>\n<div>\n\n<div itemprop=\"copy-paste-block\">\n\n<div style=\"font-size:14px\">10_convでの返信です。</div>\n\n\n<div style=\"font-size:14px\">改行付き。HTML形式(拡張ボックスを開かない)</div>\n</div>\n</div>\n</div>\n</div>"
},
“attachments": [],
“mentions": [],
“reactions": []
},
{
“id": “1565398692352",
“replyToId": “1565397760720",
“etag": “1565398692352",
“messageType": “message",
“createdDateTime": “2019-08-10T00:58:12.352Z",
“lastModifiedDateTime": null,
“deletedDateTime": null,
“subject": null,
“summary": null,
“importance": “normal",
“locale": “en-us",
“webUrl": “https://teams.microsoft.com/l/message/19%3A51b38c312f234daa9bb8e9d05aafd796%40thread.skype/1565398692352?groupId=738f192b-de2d-4cb2-bda8-0273cc7299ba&tenantId=1ae8725b-e1ff-45f3-b4ff-fb21ab4dc17f&createdTime=1565398692352&parentMessageId=1565397760720",
“policyViolation": null,
“from": {
“application": null,
“device": null,
“conversation": null,
“user": {
“id": “72c2cf01-fc7b-4b30-8020-798e87831dcc",
“displayName": “Administrator MOD",
“userIdentityType": “aadUser"
}
},
“body": {
“contentType": “html",
“content": “<div>\n<div itemprop=\"copy-paste-block\">\n\n<div style=\"font-size:14px\">10_convでの返信です。</div>\n\n\n<div style=\"font-size:14px\">改行付き。テキスト形式(拡張ボックスを開かない)</div>\n</div>\n</div>"
},
“attachments": [],
“mentions": [],
“reactions": []
},
{
“id": “1565398674509",
“replyToId": “1565397760720",
“etag": “1565398674509",
“messageType": “message",
“createdDateTime": “2019-08-10T00:57:54.509Z",
“lastModifiedDateTime": null,
“deletedDateTime": null,
“subject": null,
“summary": null,
“importance": “normal",
“locale": “en-us",
“webUrl": “https://teams.microsoft.com/l/message/19%3A51b38c312f234daa9bb8e9d05aafd796%40thread.skype/1565398674509?groupId=738f192b-de2d-4cb2-bda8-0273cc7299ba&tenantId=1ae8725b-e1ff-45f3-b4ff-fb21ab4dc17f&createdTime=1565398674509&parentMessageId=1565397760720",
“policyViolation": null,
“from": {
“application": null,
“device": null,
“conversation": null,
“user": {
“id": “72c2cf01-fc7b-4b30-8020-798e87831dcc",
“displayName": “Administrator MOD",
“userIdentityType": “aadUser"
}
},
“body": {
“contentType": “text",
“content": “10_convでの返信。HTML形式です。ただ、投稿はただのテキストでやります。"
},
“attachments": [],
“mentions": [],
“reactions": []
},
{
“id": “1565397786658",
“replyToId": “1565397760720",
“etag": “1565397786658",
“messageType": “message",
“createdDateTime": “2019-08-10T00:43:06.658Z",
“lastModifiedDateTime": null,
“deletedDateTime": null,
“subject": null,
“summary": null,
“importance": “normal",
“locale": “en-us",
“webUrl": “https://teams.microsoft.com/l/message/19%3A51b38c312f234daa9bb8e9d05aafd796%40thread.skype/1565397786658?groupId=738f192b-de2d-4cb2-bda8-0273cc7299ba&tenantId=1ae8725b-e1ff-45f3-b4ff-fb21ab4dc17f&createdTime=1565397786658&parentMessageId=1565397760720",
“policyViolation": null,
“from": {
“application": null,
“device": null,
“conversation": null,
“user": {
“id": “72c2cf01-fc7b-4b30-8020-798e87831dcc",
“displayName": “Administrator MOD",
“userIdentityType": “aadUser"
}
},
“body": {
“contentType": “text",
“content": “10_convでの返信です。"
},
“attachments": [],
“mentions": [],
“reactions": []
}
]
}
返信が降順に並んでるなぁとかいろいろ思う所はありますが、本文欄に注目してみましょう。
拡張メッセージボックスを展開せず、一行だけで記載したメッセージはHTML形式ではなくテキスト形式で保管されるようです。
“body": {
“contentType": “text",
“content": “10_convでの返信です。"
},
一方、改行を含めたメッセージを投稿すると、HTML形式になるようです。まぁ一行だけで記載したメッセージ以外はのきなみHTML形式になるのでしょうか。つーか文字の大きさはfontで定義されてるのですね。
“body": {
“contentType": “html",
“content": “<div><div>\n<div>\n\n<div itemprop=\"copy-paste-block\">\n\n<div style=\"font-size:14px\">10_convでの返信です。</div>\n\n\n<div style=\"font-size:14px\">改行付き。HTML形式(拡張ボックスを開かない)</div>\n</div>\n</div>\n</div>\n</div>"
},
絵文字なんかも再現されるようです。Unicodeで定義されてるやつなんでしょうか?
“body": {
“contentType": “html",
“content": “<div><div>\n<div>\n\n<div itemprop=\"copy-paste-block\">\n\n<div style=\"font-size:14px\">\n\n<div style=\"font-size:14px\">10_convでの返信です。いいねなどを付与します<span class=\"animated-emoticon-20-smile\" title=\"スマイル\" type=\"(smile)\"><img itemid=\"smile\" itemscope=\"\" itemtype=\"http://schema.skype.com/Emoji\" src=\"https://statics.teams.microsoft.com/evergreen-assets/skype/v2/smile/20.png\" alt=\"😄\" style=\"width:20px; height:20px\"></span></div>\n</div>\n</div>\n</div>\n</div>\n</div>"
},
いいねも取得できますが、いいねをしたユーザーはIDでしか表現されないので、名前まで表示するなら別途ユーザー情報とマッチングしなければいけないですね。
{
“reactionType": “heart",
“createdDateTime": “2019-08-10T00:58:44.29Z",
“user": {
“application": null,
“device": null,
“conversation": null,
“user": {
“id": “72c2cf01-fc7b-4b30-8020-798e87831dcc",
“displayName": null,
“userIdentityType": “aadUser"
}
…ということで、完璧なエクスポートをしようとすると
- Teamsのメッセージ種別に対してAPIでどのようにエクスポートされるかを確認
- それをどのように再現するかを設計
する必要があります。
さて、個人でこれを全てフォローするのはかなり面倒なことが分かったので、後は弊社開発部門のメンバーに丸投げしようと思います(がんばって!) 気分が向いてくれればサクっとツール作って外販できるようになってるかもしれません。あるいはAvePoint社やBitTitan社のような3rdパーティー製ソリューションを利用するのもアリですね。
まぁこの手のツールは目的から考えたクオリティにすればいいので、ひとまずメッセージが見れればいい、いいね!なんかは要らない、など要件を定義して、エクスポートする要素を限定するとツールが作りやすいと思いました。
ディスカッション
ピンバック & トラックバック一覧
[…] Teamsのチャネルメッセージを完璧にエクスポートしたい […]