Webex Teams API Client
は、Cisco Webex Teams REST API
を利用しやすくしたライブラリです。
基本的な機能のほかに、Cisco Webex TeamsのAPIを使いやすくするための機能を実装しています。
Webex Teams API Client
を使って、Cisco Webex Teams REST API
を簡単に実行できます。
- Markdownでメッセージを投稿する簡単な例
// 暗号化されたトークンをストレージから読み込む。
ProtectedString token = LoadEncryptedBotToken();
// TeamsAPIClientのインスタンスを作成する(リトライ機能付き)。
var teams = TeamsAPI.CreateVersion1Client(token, new TeamsRetryHandler(4));
// Markdownを構成します。
var markdown = new MarkdownBuilder();
markdown.Append("こんにちは、").AppendBold("Webex Teams").Append("!!");
// メッセージを投稿します。
var message = (await teams.CreateDirectMessageAsync("your_webex_teams_account@example.com", markdown.ToString())).GetData();
Console.WriteLine("メッセージが投稿されました: ID = {0}", message.Id);
- AdaptiveCardをJson文字列やobjectから生成して投稿する簡単な例
// Json文字列から生成。
var card = AdaptiveCardAttachment.FromJsonString(
@"
{
""type"": ""AdaptiveCard"",
""version"": ""1.0"",
""body"": [
{
""type"": ""TextBlock"",
""text"": ""Adaptive Cards"",
""size"": ""large""
}
],
""actions"": [
{
""type"": ""Action.OpenUrl"",
""url"": ""http://adaptivecards.io"",
""title"": ""Learn More""
}
]
}
");
// カード付きでメッセージを投稿。
var message = (await teams.CreateDirectMessageAsync("your_webex_teams_account@example.com",
"[Learn More](http://adaptivecards.io) about Adaptive Card.",
card)).GetData();
Console.WriteLine("Message was posted: ID = {0}", message.Id);
// 匿名型やクラスのインスタンスからも生成できます。
var cardObj = new
{
type = "AdaptiveCard",
version = "1.0",
body = new []
{
new
{
type = "TextBlock",
text = "Adaptive Cards",
size = "large",
}
},
actions = new []
{
new
{
type = "Action.OpenUrl",
url = "http://adaptivecards.io",
title = "Learn More"
}
},
};
// objectから生成。
card = AdaptiveCardAttachment.FromObject(cardObj);
// カード付きでメッセージを投稿。
message = (await teams.CreateDirectMessageAsync("your_webex_teams_account@example.com",
"[Learn More](http://adaptivecards.io) about Adaptive Card.",
card)).GetData();
Console.WriteLine("Message was posted: ID = {0}", message.Id);
// teams.GetAttachmentActionAsync()でユーザからの入力を受け取れます。
// 本APIClientのWebhook listner, event handlerには、これらの入力を受け取る機能があります。
- ゲストユーザ用のアカウントを発行して投稿する例
Webex TeamsのGuest Issuer機能を利用する例です。
// 暗号化されたGuest Issuer secretをストレージから読み込む。
ProtectedString secret = LoadEncryptedGuestIssuerSecret();
// GuestIssuerClientのインスタンスを作成する。
var guestIssuer = TeamsAPI.CreateVersion1GuestIssuerClient(secret, "your_guest_issuer_id");
// Guest Userを作成する。
var guest = (await guestIssuer.CreateGuestUserAsync("my-guest-id", "ゲストユーザ名")).GetData();
// ゲストユーザ用のTeamsAPIClientインスタンスを作成する(リトライ機能付き)。
var teams = TeamsAPI.CreateVersion1Client(guest, new TeamsRetryHandler(4));
// ゲストユーザからメッセージを投稿する。
var message = (await teams.CreateDirectMessageAsync("your_webex_teams_account@example.com", "こんにちは、私はゲストユーザです!!")).GetData();
Console.WriteLine("メッセージが投稿されました: ID = {0}", message.Id);
- Pagination機能を利用する例
Webex Temas REST APIのPagination機能の例が以下になります。
スペースを50個ずつ取得していって、特定の名前のスペースへ「こんにちは」を投稿します。
// すべてのGroupスペースを取得するためにEnumeratorを取得します。
var e = (await teams.ListSpacesAsync(
type: SpaceType.Group,
max: 50)
).GetListResultEnumerator();
// すべてのスペースを取得するまで繰り返します。
while (await e.MoveNextAsync())
{
var r = e.CurrentResult;
if (r.IsSuccessStatus && r.Data.HasItems)
{
// それぞれの結果にスペースのリストが含まれます。
foreach (var space in r.Data.Items)
{
// 特定の名前のスペースに、「こんにちは」と投稿します。
if (space.Title == "Webex Teams API Client(Thrzn41.WebexTeams)のデモ用スペース")
{
await teams.CreateMessageAsync(space, "こんにちは、Webex Teams!!");
}
}
}
}
ほかのサンプルは、 こちら。
- .NET Standard 1.3以降
- .NET Core 1.0以降
- .NET Framework 4.5.2以降
注記: 簡易Webhookサーバ機能を利用する場合は、
.NET Standard 2.0+, .NET Core 2.0+, .NET Framework 4.5.2+が必要です。
Webex Teams API Clientのサンプルは、 こちらをクリック。
- Webex Teamsの基本的なAPI(List/Get/Create Message, Spaceなど)。
- Webex TeamsのAdmin API(List/Get Event, Licenseなど)。
- ストレージに保存するTokenの暗号化と復号。
- AdaptiveCardsの添付(カードの作成やユーザ入力の取得)。
- List API用のPagination機能。Paginationを簡単にするためのEnumerator。
- Retry-after値の処理とRetry handler。
- Markdown builder。
- エラーコードや詳細の取得。
- Webhook secretの検証とWebhook notification manager、Webhook event handler。
- Guest Issuer helper。
- OAuth2 helper。
- 簡易Webhookサーバ機能(.NET Standard 2.0+, .NET Core 2.0+, .NET Framework 4.5.2+)。
Teamsのリソース名 | 利用可能な機能 | 説明 |
---|---|---|
Person/People | List/Get | v1.2.2から利用可能。Get Meも利用可能 |
Space(Room) | List/Create/Get/Update/Delete | v1.2.2から利用可能。Roomは、API Clientでは、Spaceと呼ばれる。 |
Space(Room) Meeting Info | Get | v1.7.1から利用可能。 |
SpaceMembership(Membership) | List/Create/Get/Update/Delete | v1.2.2から利用可能。Membershipは、API Clientでは、SpaceMembershipと呼ばれる。 |
Message | List/Create/Get/Delete | v1.2.2から利用可能。ローカルのstreamからファイル添付も可能 |
AdaptiveCards | List/Create/Get/Delete | v1.7.1から利用可能。 |
AttachmentActions | Create/Get | v1.7.1から利用可能。 |
Team | List/Create/Get/Update/Delete | v1.2.2から利用可能。 |
TeamMembership | List/Create/Get/Update/Delete | v1.2.2から利用可能。 |
Webhook | List/Create/Get/Update/Delete | v1.2.2から利用可能。 |
File | GetInfo/GetData/Upload | v1.2.2から利用可能。 |
Place/Device/xAPI | - | v1.8.1で計画中。 |
Teamsのリソース名 | 利用可能な機能 | 説明 |
---|---|---|
Person/People | Create/Update/Delete | v1.2.2から利用可能。 |
Event | List/Get | v1.2.2から利用可能。 |
Organization | List/Get | v1.2.2から利用可能。 |
License | List/Get | v1.2.2から利用可能。 |
Role | List/Get | v1.2.2から利用可能。 |
GroupResource | List/Get | v1.2.2から利用可能。 |
GroupResourceMembership | List/Get/Update | v1.2.2から利用可能。 |
ProtectedString
が、Token保存時の暗号化と、読み込み時の復号の機能を提供します。
詳細は後述。
Cisco Webex Teams APIのpaginationに関しては、ここを参照。
result.HasNext
とresult.ListNextAsync()
が、Webex Teams API Clientで利用可能です。
詳細は後述。
result.HasRetryAfter
と result.RetryAfter
が、Webex Teams API Clientで利用可能です。
また、 TeamsRetryHandler
とTeamsRetryOnErrorHandler
が利用可能です。
詳細は後述。
result.HttpStatusCode
が、Webex Teams API Clientで利用可能です。
詳細は後述。
Cisco Webex Teams APIは、エラーコードと詳細を返す場合があります。
result.Data.HasErrors
とresult.Data.GetErrors()
が、Webex Teams API Clientで利用可能です。
Cisco Webex Teams APIは、部分的なエラーを返す場合があります。
部分エラーの詳細に関しては、ここを参照。
Item.HasErrors
とItem.GetPartialErrors()
が、Webex Teams API Clientで利用可能です。
trackingIdは、Cisco Webex Teams APIのテクニカルサポートで利用される可能性があります。
result.TrackingId
が、Webex Teams API Clientで利用可能です。
詳細は後述。
Webhook.CreateEventValidator()
が、Webex Teams API Clientで利用可能です。
詳細は後述。
Webex Teams API ClientのCreateWebhookAsync()
メソッドはデフォルトでは、webhook secretを動的に生成します。
Webhook listener機能は、簡易的なWebhookのサーバ機能を提供します。
注記: この機能は、簡単なテスト時の利用を想定しています。
運用環境等では、より信頼性のあるサーバをご利用ください。
WebhookListener
が、Webex Teams API Clientで利用可能です。
詳細は後述。
Webex Teams API Client
は、以下のいずれかの方法で、NuGet
package manager経由で入手できます。
-
NuGet Package ManagerのGUI
"Thrzn41.WebexTeams
"を検索してインストール。 -
NuGet Package ManagerのCLI
PM> Install-Package Thrzn41.WebexTeams
- .NET Client
> dotnet add package Thrzn41.WebexTeams
usingディレクティブを利用する場合は、以下の名前空間を指定します。
using Thrzn41.Util
using Thrzn41.WebexTeams
using Thrzn41.WebexTeams.Version1
必要に応じて、以下も利用可能です。
- Guest Issuer helper利用時、
Thrzn41.WebexTeams.Version1.GuestIssuer
- OAuth2 helper利用時、
Thrzn41.WebexTeams.Version1.OAuth2
- Admin API利用時、
Thrzn41.WebexTeams.Version1.Admin
Webex Teams API Clientのインスタンスは可能な限り長い期間使いまわすようにします。
/// 基本API利用時。
TeamsAPIClient teams = TeamsAPI.CreateVersion1Client(token);
Admin APIを利用する場合は、Admin API用のインスタンスを作成する必要があります。
TeamsAdminAPIClient
は、TeamsAPIClient
の全機能に加えて、Adminの機能が利用できます。
/// Admin API利用時。
TeamsAdminAPIClient teams = TeamsAPI.CreateVersion1AdminClient(token);
注記: 'token'は、Cisco Webex Teams APIでは、非常にセンシティブな情報です。
'token'は、慎重に保護する必要があります。
ソースコード中に直接記載したり、安全ではない方法で保存しないようにします。
Webex Teams API Client
では、トークンを暗号化したり復号する方法を、いくつか提供しています。
独自の方法で暗号化や復号、保護を実装する場合は、インスタンス作成時に、復号されたトークン文字列を利用することができます。
char[] tokens = GetBotTokenFromBotOwner();
var protectedToken = LocalProtectedString.FromChars(tokens);
LocalProtectedString.ClearChars(tokens);
Save("token.dat", protectedToken.EncryptedData);
Save("entropy.dat", protectedToken.Entropy);
注記: LocalProtectedStringはメモリ内での保護は提供していません。
Tokenを保存する際の、暗号化と復号での利用を想定しています。
byte[] encryptedData = Load("token.dat");
byte[] entropy = Load("entropy.dat");
var protectedToken = LocalProtectedString.FromEncryptedData(encryptedData, entropy);
/// 基本API利用時。
TeamsAPIClient teams = TeamsAPI.CreateVersion1Client(protectedToken);
注記: オプションに応じて、暗号化されたデータは、暗号化したときと同じローカルユーザまたはローカルマシン上でのみ復号できます。
var result = await teams.CreateMessageAsync("xyz_space_id", "こんにちは, Teams!");
if(result.IsSuccessStatus)
{
Console.WriteLine("メッセージが投稿されました: id = {0}", result.Data.Id);
}
result.IsSuccessStatus
を使って成功したかどうか確認できます。
result.Data.HasErrors
やresult.Data.GetErrorMessage()
を使ってCisco Webex Teams APIサービスからエラーコード、エラーメッセージを受け取ることができます。
var result = await teams.CreateMessageAsync("xyz_space_id", "こんにちは, Teams!");
if(result.IsSuccessStatus)
{
Console.WriteLine("メッセージが投稿されました: id = {0}", result.Data.Id);
}
else
{
Console.WriteLine("メッセージの投稿に失敗しました: status = {0}, trackingId = {1}", result.HttpStatusCode, result.TrackingId);
if(result.Data.HasErrors)
{
Console.WriteLine( result.Data.GetErrorMessage() );
}
}
例外を捕捉したい場合は、データ取得時に、result.GetData()
を利用することができます。
result.GetData()
は、リクエスト失敗時にTeamsResultException
をスローします。
(一方、result.Data
は、TeamsResultException
を送出しません。)
try
{
var result = await teams.CreateMessageAsync("xyz_space_id", "こんにちは, Teams!");
var message = result.GetData();
Console.WriteLine("メッセージが投稿されました: id = {0}", message.Id);
}
catch(TeamsResultException tre)
{
Console.WriteLine("メッセージの投稿に失敗しました: status = {0}, trackingId = {1}, description = {2}",
tre.HttpStatusCode, tre.TrackingId, tre.Message);
}
using (var fs = new FileStream("path/myfile.png", FileMode.Open, FileAccess.Read, FileShare.Read))
using (var data = new TeamsFileData(fs, "imagefile.png", TeamsMediaType.ImagePNG))
{
var result = await teams.CreateMessageAsync("xyz_space_id", "添付ファイル付き", data);
if(result.IsSuccessStatus)
{
Console.WriteLine("添付ファイル付きでメッセージが投稿されました: id = {0}", result.Data.Id);
}
}
var result = await teams.CreateDirectMessageAsync("targetuser@example.com", "こんにちは, Teams!");
if(result.IsSuccessStatus)
{
Console.WriteLine("メッセージが投稿されました: id = {0}", result.Data.Id);
}
var result = await teams.ListSpacesAsync();
if(result.IsSuccessStatus && result.Data.HasItems)
{
foreach (var item in result.Data.Items) {
Console.WriteLine("Space: title = {0}", item.Title);
}
}
ダウンロードせずにファイルの情報だけ入手する。
var result = await teams.GetFileInfoAsync(new Uri("https://api.example.com/path/to/file.png"));
if(result.IsSuccessStatus)
{
var file = result.Data;
Console.WriteLine("File: Name = {0}, Size = {1}, Type = {2}", file.Name, file.Size?.Value, file.MediaType?.Name);
}
ファイルをダウンロードする。
この例では、ファイルのデータがMemoryStreamにコピーされます。
using(var stream = new MemoryStream())
{
var result = await teams.CopyFileDataToStreamAsync(new Uri("https://api.example.com/path/to/file.png"), stream);
if(result.IsSuccessStatus)
{
var file = result.Data;
Console.WriteLine("File: Name = {0}, Size = {1}, Type = {2}", file.Name, file.Size?.Value, file.MediaType?.Name);
}
}
var result = await teams.ListSpacesAsync();
if(result.IsSuccessStatus)
{
//
// ここで何か処理する...
//
if(result.HasNext)
{
// 続きのリストがあれば取得する。
result = await result.ListNextAsync();
if(result.IsSuccessStatus)
{
// ...
}
}
}
// すべてのGroupスペースを取得するためにEnumeratorを取得します。
var e = (await teams.ListSpacesAsync(
type: SpaceType.Group,
max: 50)
).GetListResultEnumerator();
// すべてのスペースを取得するまで繰り返します。
while (await e.MoveNextAsync())
{
var r = e.CurrentResult;
if (r.IsSuccessStatus && r.Data.HasItems)
{
// それぞれの結果にスペースのリストが含まれます。
foreach (var space in r.Data.Items)
{
Console.WriteliLine("Title = {0}", space.Title);
}
}
}
var result = await teams.ListSpacesAsync();
Console.WriteLine("Status is {0}", result.HttpStatusCode);
var result = await teams.ListSpacesAsync();
if(result.IsSuccessStatus)
{
//
// ここで何かする...
//
}
else if(result.HasRetryAfter)
{
Console.WriteLine("{0}後にリトライしなきゃ!!", result.RetryAfter.Delta);
}
TeamsRetryHandler
とTeamsRetryOnErrorHandler
を利用してリトライ処理を容易にします。
// リトライ機能付きでインスタンスを作成(この場合は、最大4回のリトライ)。
var teams = TeamsAPI.CreateVersion1Client(token, new TeamsRetryHandler(4));
// リクエスト時に、HTTP 429のレスポンスがあると、リトライが行われます。
var result = await teams.GetMeAsync();
//HTTP 500, 502, 503, 504レスポンス時にもリトライしたい場合は、TeamsRetryOnErrorHandlerを利用します。
// Retry-Afterヘッダがない場合は、この例の場合は、15秒後にリトライが行われます。
var teams = TeamsAPI.CreateVersion1Client(token, new TeamsRetryOnErrorHandler(4, TimeSpan.FromSeconds(15.0f)));
var result = await teams.ListSpacesAsync();
Console.WriteLine("Tracking id: {0}", result.TrackingId);
var md = new MarkdownBuilder();
// メンションと番号付きリストでMarkdownを作成。
md.Append("こんにちは、").AppendMentionToPerson("xyz_person_id", "〇〇さん").AppendLine();
md.AppendOrderedList("Item1");
md.AppendOrderedList("Item2");
md.AppendOrderedList("Item3");
var result = await teams.CreateMessageAsync("xyz_space_id", md.ToString());
var webhook = await teams.GetWebhookAsync("xyz_webhook_id");
var validator = webhook.CreateEventValidator();
イベントがWebhookのURIに通知された際には、 X-Spark-Signatureがハッシュ値を持っています。
validatorを利用して、データの整合性を確認できます。
byte[] webhookEventData = GetWebhookEventData();
if( validator.Validate(webhookEventData, "xyz_x_teams_signature_value") )
{
Console.WriteLine("通知されたイベントデータの検証に成功!");
}
Webhook notification managerを使ってWebhookへの通知を管理します。
- インスタンスを作成する。
var notificationManager = new WebhookNotificationManager();
- 通知用のfunctionを登録します。
var webhook = await teams.GetWebhookAsync("xyz_webhook_id");
notificationManager.AddNotification(
webhook,
(eventData) =>
{
Console.WriteLine("イベントを受信, id = {0}", eventData.Id);
}
);
- イベントの受信時。
byte[] webhookEventData = GetWebhookEventData();
// Signatureが確認され登録したfunctionにイベントデータが通知されます。
notificationManager.ValidateAndNotify(webhookEventData, "xyz_x_teams_signature_value", encodingOfData);
- Webhook listenerのインスタンスの作成。
var listener = new WebhookListener();
- 待ち受けする、ホストとポートを登録する。
待ち受けには、TLS/httpsを利用すべきです。
そのためには、まず、実行環境側でnetsh
ツールなどを利用して、有効な証明書をバインドしておく必要があります。
バインドされたアドレスとポート番号で、エンドポイントを追加します。
var endpointUri = listener.AddListenerEndpoint("yourwebhookserver.example.com", 8443);
- Webhook listener用のWebhookを作成します。
listener.AddListenerEndpoint()
が返すendpointUri
がWebhookの通知先Uriになります。
var result = await teams.CreateWebhookAsync(
"my webhook for test",
endpointUri,
EventResource.Message,
EventType.Created);
- Webhook listenerにWebhookと通知先funcを登録します。
var webhook = result.Data;
listener.AddNotification(
webhook,
async (eventData) =>
{
Console.WriteLine("Eventが通知されました, id = {0}", eventData.Id);
if(eventData.Resource == EventResource.Message)
{
Console.WriteLine("Message, id = {0}", eventData.MessageData.Id);
}
}
);
- Listenerの開始。
Listenerを開始すると、イベント発生時に登録したfunctionに通知されます。
listener.Start();
グローバルIPアドレスが利用できない場合、 ngrokなどのトンネリングサービスが便利な場合があります。
- ngrokの入手と起動。
ngrokのコマンドラインツールは、ここから入手できます。
以下のコマンドで、トンネリングサービスを起動して、localhostの8080ポートにフォワードされます。
prompt> ngrok http 8080 --bind-tls=true
- Webhook listenerのインスタンスの作成。
var listener = new WebhookListener();
- 待ち受けする、ホストとポートを登録する。
ngrokは、localhost
へフォワードします。
var endpointUri = listener.AddListenerEndpoint("localhost", 8080, false);
- Webhook listener用のWebhookを作成します。
この例では、ngronのトンネリングサービスを利用しています。
listener.AddListenerEndpoint()
が返したUriは、フォワード先のUriです。
Webhookには、ngrok側のUriを指定する必要があります。
ngrokが、https://ngrok-xyz.example.com
を割り当てた場合、
String.Format("https://ngrok-xyz.example.com{0}", endpointUri.AbsolutePath)
をWebhookの宛先として指定します。
var result = await teams.CreateWebhookAsync(
"テスト用のWebhook",
new Uri(String.Format("https://ngrok-xyz.example.com{0}", endpointUri.AbsolutePath)),
EventResource.Message,
EventType.Created);
- Webhook listenerにWebhookと通知先funcを登録します。
var webhook = result.Data;
listener.AddNotification(
webhook,
async (eventData) =>
{
Console.WriteLine("Eventが通知されました, id = {0}", eventData.Id);
if(eventData.Resource == EventResource.Message)
{
Console.WriteLine("Message, id = {0}", eventData.MessageData.Id);
}
}
);
- Listenerの開始。
Listenerを開始すると、イベント発生時に登録したfunctionに通知されます。
listener.Start();