Skip to content

Latest commit

 

History

History
619 lines (442 loc) · 19.9 KB

README.ja-JP.md

File metadata and controls

619 lines (442 loc) · 19.9 KB

※重要な告知※

Cisco Sparkに別れを告げて、Cisco Webex Teamsを始めてみよう。

Cisco Sparkは、Cisco Webex Teamsになりました。

Thrzn41.CiscoSparkも、Thrzn41.WebexTeamsになりました。
リポジトリは、 WebexTeamsAPIClientへ移行しています。

https://github.com/thrzn41/WebexTeamsAPIClient

branchやforkではなく、完全に移行することにしました。

[Obsoleted] Cisco Spark API Client for .NET

nuget MIT license

Cisco Spark API Clientは、Cisco Spark REST APIを利用しやすくしたライブラリです。
基本的な機能のほかに、Cisco SparkのAPIを使いやすくするための機能を実装しています。

ほかの言語のREADME


利用可能なプラットフォーム

  • .NET Standard 1.3以降
  • .NET Core 1.0以降
  • .NET Framework 4.5.2以降

注記: 簡易Webhookサーバ機能を利用する場合は、
.NET Stardard 2.0+, .NET Core 2.0+, .NET Framework 4.5.2+が必要です。


利用可能な機能

  • Cisco Sparkの基本的なAPI(List/Get/Create Message, Spaceなど)。
  • Cisco SparkのAdmin API(List/Get Event, Licenseなど)。
  • ストレージに保存するTokenの暗号化と復号。
  • List API用のPagination機能。
  • Retry-after値の処理とRetry executor。
  • Markdown builder
  • エラーコードや詳細の取得。
  • Webhook secretの検証とWebhook notification manager、Webhook event handler。
  • OAuth2 helper
  • 簡易Webhookサーバ機能(.NET Standard 2.0+, .NET Core 2.0+, .NET Framework 4.5.2+)。

基本機能

Sparkのリソース名 利用可能な機能 説明
Person/People List/Get 利用可能。Get Meも利用可能
Space(Room) List/Create/Get/Update/Delete 利用可能。Roomは、API Clientでは、Spaceと呼ばれる。
SpaceMembership(Membership) List/Create/Get/Update/Delete 利用可能。Membershipは、API Clientでは、SpaceMembershipと呼ばれる。
Message List/Create/Get/Delete 利用可能。ローカルのstreamからファイル添付も可能
Team List/Create/Get/Update/Delete 利用可能。
TeamMembership List/Create/Get/Update/Delete 利用可能。
Webhook List/Create/Get/Update/Delete 利用可能。
File GetInfo/GetData/Upload 利用可能。

Admin機能

Sparkのリソース名 利用可能な機能 説明
Person/People Create/Update/Delete 利用可能。
Event List/Get 利用可能。
Organization List/Get 利用可能。
License List/Get 利用可能。
Role List/Get 利用可能。

ストレージのTokenの暗号化と復号

ProtectedStringが、Token保存時の暗号化と、読み込み時の復号の機能を提供します。
詳細は後述。

Pagination機能

Cisco Spark APIのpaginationに関しては、ここを参照。

result.HasNextresult.ListNextAsync()が、Cisco Spark API Clientで利用可能です。
詳細は後述。

Retry-Afterの取得

result.HasRetryAfterresult.RetryAfterが、Cisco Spark API Clientで利用可能です。
また、 RetryExecutorが利用可能です。
詳細は後述。

HTTP Statusコードの取得

result.HttpStatusCodeが、Cisco Spark API Clientで利用可能です。
詳細は後述。

エラーコードと詳細の取得

Cisco Spark APIは、エラーコードと詳細を返す場合があります。
result.Data.HasErrorsresult.Data.GetErrors()が、Cisco Spark API Clientで利用可能です。

部分エラーの取得

Cisco Spark APIは、部分的なエラーを返す場合があります。
部分エラーの詳細に関しては、ここを参照。
Item.HasErrorsItem.GetPartialErrors()が、Cisco Spark API Clientで利用可能です。

trackingIdの取得

trackingIdは、Cisco Spark APIのテクニカルサポートで利用される可能性があります。

result.TrackingIdが、Cisco Spark API Clientで利用可能です。
詳細は後述。

Validates webhook secret

Webhook.CreateEventValidator()が、Cisco Spark API Clientで利用可能です。
詳細は後述。

Cisco Spark API ClientのCreateWebhookAsync()メソッドはデフォルトでは、webhook secretを動的に生成します。

Webhook listener(.NET Standard 2.0+, .NET Core 2.0+, .NET Framework 4.5.2+)

Webhook listener機能は、簡易的なWebhookのサーバ機能を提供します。

注記: この機能は、簡単なテスト時の利用を想定しています。
運用環境等では、より信頼性のあるサーバをご利用ください。

WebhookListenerが、Cisco Spark API Clientで利用可能です。
詳細は後述。


基本的な使い方

Cisco Spark API Clientのインストール

Cisco Spark API Clientは、以下のいずれかの方法で、NuGet package manager経由で入手できます。

  • NuGet Package ManagerのGUI
    "Thrzn41.CiscoSpark"を検索してインストール。

  • NuGet Package ManagerのCLI

PM> Install-Package Thrzn41.CiscoSpark
  • .NET Client
> dotnet add package Thrzn41.CiscoSpark

Cisco Spark API Client関連のusingディレクティブ

usingディレクティブを利用する場合は、以下の名前空間を指定します。

using Thrzn41.Util
using Thrzn41.CiscoSpark
using Thrzn41.CiscoSpark.Version1

必要に応じて、Thrzn41.CiscoSpark.Version1.Adminも利用可能です。

Cisco Spark API Clientインスタンスの作成

Cisco Spark API Clientのインスタンスは可能な限り長い期間使いまわすようにします。

/// 基本API利用時。
SparkAPIClient spark = SparkAPI.CreateVersion1Client(token);

Admin APIを利用する場合は、Admin API用のインスタンスを作成する必要があります。
SparkAdminAPIClientは、SparkAPIClientの全機能に加えて、Adminの機能が利用できます。

/// Admin API利用時。
SparkAdminAPIClient spark = SparkAPI.CreateVersion1AdminClient(token);

注記: 'token'は、Cisco Spark APIでは、非常にセンシティブな情報です。
'token'は、慎重に保護する必要があります。
ソースコード中に直接記載したり、安全ではない方法で保存しないようにします。
Cisco Spark API Clientでは、トークンを暗号化したり復号する方法を、いくつか提供しています。
独自の方法で暗号化や復号、保護を実装する場合は、インスタンス作成時に、復号されたトークン文字列を利用することができます。

暗号化したTokenをストレージに保存する

char[] tokens = GetBotTokenFromBotOwner();

var protectedToken = LocalProtectedString.FromChars(tokens);
LocalProtectedString.ClearChars(tokens);

Save("token.dat",   protectedToken.EncryptedData);
Save("entropy.dat", protectedToken.Entropy);

注記: LocalProtectedStringはメモリ内での保護は提供していません。
Tokenを保存する際の、暗号化と復号での利用を想定しています。

暗号化したTokenをストレージから読み込んで、Cisco Spark API Clientのインスタンスを作成する

byte[] encryptedData = Load("token.dat");
byte[] entropy       = Load("entropy.dat");

var protectedToken = LocalProtectedString.FromEncryptedData(encryptedData, entropy);

/// 基本API利用時。
SparkAPIClient spark = SparkAPI.CreateVersion1Client(protectedToken);

注記: オプションに応じて、暗号化されたデータは、暗号化したときと同じローカルユーザまたはローカルマシン上でのみ復号できます。

Cisco Sparkのスペースにメッセージを投稿する

var result = await spark.CreateMessageAsync("xyz_space_id", "こんにちは, Spark!");

if(result.IsSuccessStatus)
{
   Console.WriteLine("メッセージが投稿されました: id = {0}", result.Data.Id);
}

成功, 失敗, エラーのハンドリング

result.IsSuccessStatusを使って成功したかどうか確認できます。
result.Data.HasErrorsresult.Data.GetErrorMessage()を使ってCisco Spark APIサービスからエラーコード、エラーメッセージを受け取ることができます。

var result = await spark.CreateMessageAsync("xyz_space_id", "こんにちは, Spark!");

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()は、リクエスト失敗時にSparkResultExceptionをスローします。
(一方、result.Dataは、SparkResultExceptionを送出しません。)

try
{
  var result = await spark.CreateMessageAsync("xyz_space_id", "こんにちは, Spark!");

  var message = result.GetData();

  Console.WriteLine("メッセージが投稿されました: id = {0}", message.Id);
}
catch(SparkResultException sre)
{
  Console.WriteLine("メッセージの投稿に失敗しました: status = {0}, trackingId = {1}, description = {2}",
                      sre.HttpStatusCode, sre.TrackingId, sre.Message);
}

Cisco Sparkのスペースに添付ファイル付きでメッセージを投稿する

using (var fs   = new FileStream("path/myfile.png", FileMode.Open, FileAccess.Read, FileShare.Read))
using (var data = new SparkFileData(fs, "imagefile.png", SparkMediaType.ImagePNG))
{
    var result = await spark.CreateMessageAsync("xyz_space_id", "添付ファイル付き", data);

    if(result.IsSuccessStatus)
    {
       Console.WriteLine("添付ファイル付きでメッセージが投稿されました: id = {0}", result.Data.Id);
    }
}

Cisco Sparkの1:1スペースにメッセージを投稿する

var result = await spark.CreateDirectMessageAsync("[email protected]", "こんにちは, Spark!");

if(result.IsSuccessStatus)
{
   Console.WriteLine("メッセージが投稿されました: id = {0}", result.Data.Id);
}

スペースの一覧を取得する

var result = await spark.ListSpacesAsync();

if(result.IsSuccessStatus && result.Data.HasItems)
{
  foreach (var item in result.Data.Items) {
    Console.WriteLine("Space: title = {0}", item.Title);
  }  
}

ファイルの情報やデータを取得する

ダウンロードせずにファイルの情報だけ入手する。

var result = await spark.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);
}

ファイルをダウンロードする。

var result = await spark.GetFileDataAsync(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);

  using(var stream = file.Stream)
  {
    // streamにファイルのデータが含まれる。
  }
}

Pagenation機能を利用する

var result = await spark.ListSpacesAsync();

if(result.IsSuccessStatus)
{
  //
  // ここで何か処理する...
  //

  if(result.HasNext)
  {
    // 続きのリストがあれば取得する。
    result = await result.ListNextAsync();

    if(result.IsSuccessStatus)
    {
      // ...
    }
  }
}

Http status codeを取得する

var result = await spark.ListSpacesAsync();

Console.WriteLine("Status is {0}", result.HttpStatusCode);

Retry afterを取得する.

var result = await spark.ListSpacesAsync();

if(result.IsSuccessStatus)
{
  //
  // ここで何かする...
  //
}
else if(result.HasRetryAfter)
{
  Console.WriteLine("{0}後にリトライしなきゃ!!", result.RetryAfter.Delta);  
}

Retry Executor

RetryExecutorを利用してリトライ処理を容易にします。

// RetryExecutor.Oneは最大で1回のリトライを試みます。
var result = RetryExecutor.One.ListAsync(
  () =>
  {
      // このメソッドは必要に応じて、リトライされます。
      return spark.ListSpacesAsync();
  },

  (r, retryCount) =>
  {
      // ここは、リトライが実行される前に呼び出されます。

      // ここでリトライ時のログの出力等の処理が可能です。
      Log.Info("Retry is required: delta = {0}, counter = {1}", r.RetryAfter.Delta, retryCount);

      // 'true'を返すとリトライが実行されます。
      return true;
  }
);

TrackingIdを取得する

var result = await spark.ListSpacesAsync();

Console.WriteLine("Tracking id: {0}", result.TrackingId);  

Markdown Builder.

var md = new MarkdownBuilder();

// メンションと番号付きリストでMarkdownを作成。
md.Append("こんにちは、").AppendMentionToPerson("xyz_person_id", "〇〇さん").AppendLine();
md.AppendOrderedList("Item1");
md.AppendOrderedList("Item2");
md.AppendOrderedList("Item3");

var result = await spark.CreateMessageAsync("xyz_space_id", md.ToString());

Webhookに通知されたデータを検証する

var webhook = await spark.GetWebhookAsync("xyz_webhook_id");

var validator = webhook.CreateEventValidator();

イベントがWebhookのURIに通知された際には、 x-Spark-Signatureがハッシュ値を持っています。

validatorを利用して、データの整合性を確認できます。

byte[] webhookEventData = GetWebhookEventData();

if( validator.Validate(webhookEventData, "xyz_x_spark_signature_value") )
{
  Console.WriteLine("通知されたイベントデータの検証に成功!");
}

Webhookの通知管理

Webhook notification managerを使ってWebhookへの通知を管理します。

  • インスタンスを作成する。
var notificationManager = new WebhookNotificationManager();
  • 通知用のfunctionを登録します。
var webhook = await spark.GetWebhookAsync("xyz_webhook_id");

notificationManager.AddNotification(
  webhook,
  (eventData) =>
  {
    Console.WriteLine("イベントを受信, id = {0}", eventData.Id);
  }
);
  • イベントの受信時。
byte[] webhookEventData = GetWebhookEventData();

// Signatureが確認され登録したfunctionにイベントデータが通知されます。
notificationManager.ValidateAndNotify(webhookEventData, "xyz_x_spark_signature_value", encodingOfData);

Webhook Listener

  • 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 spark.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.Resouce == EventResouce.Message)
    {
      Console.WriteLine("Message, id = {0}", eventData.MessageData.Id);
    }
  }
);
  • Listenerの開始。

Listenerを開始すると、イベント発生時に登録したfunctionに通知されます。

listener.Start();

Webhook listenerをngrokと共に利用する

グローバル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 spark.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.Resouce == EventResouce.Message)
    {
      Console.WriteLine("Message, id = {0}", eventData.MessageData.Id);
    }
  }
);
  • Listenerの開始。

Listenerを開始すると、イベント発生時に登録したfunctionに通知されます。

listener.Start();