{"id":23388309,"url":"https://github.com/thrzn41/ciscosparkapiclient","last_synced_at":"2026-05-18T06:12:07.996Z","repository":{"id":97061641,"uuid":"113023125","full_name":"thrzn41/CiscoSparkAPIClient","owner":"thrzn41","description":"[Obsoleted] Cisco Spark API Client for .NET","archived":false,"fork":false,"pushed_at":"2018-06-04T08:16:22.000Z","size":224,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2025-04-08T13:23:00.808Z","etag":null,"topics":["cisco-spark","ciscospark","csharp","dotnet","dotnet-core","dotnet-framework","dotnet-standard"],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/thrzn41.png","metadata":{"files":{"readme":"README.ja-JP.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2017-12-04T09:34:13.000Z","updated_at":"2018-06-04T08:16:48.000Z","dependencies_parsed_at":null,"dependency_job_id":"f4839c56-ce07-439c-a2c8-60f0dc7bd39a","html_url":"https://github.com/thrzn41/CiscoSparkAPIClient","commit_stats":null,"previous_names":[],"tags_count":4,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thrzn41%2FCiscoSparkAPIClient","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thrzn41%2FCiscoSparkAPIClient/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thrzn41%2FCiscoSparkAPIClient/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thrzn41%2FCiscoSparkAPIClient/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/thrzn41","download_url":"https://codeload.github.com/thrzn41/CiscoSparkAPIClient/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247848497,"owners_count":21006261,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cisco-spark","ciscospark","csharp","dotnet","dotnet-core","dotnet-framework","dotnet-standard"],"created_at":"2024-12-22T02:18:46.540Z","updated_at":"2025-10-03T17:23:12.409Z","avatar_url":"https://github.com/thrzn41.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"# ※重要な告知※\n\nCisco Sparkに別れを告げて、**Cisco Webex Teams**を始めてみよう。\n\nCisco Sparkは、**Cisco Webex Teams**になりました。\n\n`Thrzn41.CiscoSpark`も、`Thrzn41.WebexTeams`になりました。  \nリポジトリは、 [WebexTeamsAPIClient](https://github.com/thrzn41/WebexTeamsAPIClient)へ移行しています。\n\nhttps://github.com/thrzn41/WebexTeamsAPIClient\n\n\u003e branchやforkではなく、完全に移行することにしました。\n\n# [Obsoleted] Cisco Spark API Client for .NET\n\n[![nuget](https://img.shields.io/nuget/v/Thrzn41.CiscoSpark.svg)](https://www.nuget.org/packages/Thrzn41.CiscoSpark) [![MIT license](https://img.shields.io/github/license/thrzn41/CiscoSparkAPIClient.svg)](https://github.com/thrzn41/CiscoSparkAPIClient/blob/master/LICENSE)\n\n`Cisco Spark API Client`は、`Cisco Spark REST API`を利用しやすくしたライブラリです。  \n基本的な機能のほかに、Cisco SparkのAPIを使いやすくするための機能を実装しています。\n\n#### ほかの言語のREADME\n* [English README](https://github.com/thrzn41/CiscoSparkAPIClient/blob/master/README.md) ([英語のREADME](https://github.com/thrzn41/CiscoSparkAPIClient/blob/master/README.md))\n\n---\n## 利用可能なプラットフォーム\n* .NET Standard 1.3以降\n* .NET Core 1.0以降\n* .NET Framework 4.5.2以降\n\n\u003e 注記: 簡易Webhookサーバ機能を利用する場合は、  \n\u003e .NET Stardard 2.0+, .NET Core 2.0+, .NET Framework 4.5.2+が必要です。\n\n\n---\n## 利用可能な機能\n\n* Cisco Sparkの基本的なAPI(List/Get/Create Message, Spaceなど)。\n* Cisco SparkのAdmin API(List/Get Event, Licenseなど)。\n* ストレージに保存するTokenの暗号化と復号。\n* List API用のPagination機能。\n* Retry-after値の処理とRetry executor。\n* Markdown builder\n* エラーコードや詳細の取得。\n* Webhook secretの検証とWebhook notification manager、Webhook event handler。\n* OAuth2 helper\n* 簡易Webhookサーバ機能(.NET Standard 2.0+, .NET Core 2.0+, .NET Framework 4.5.2+)。\n\n### 基本機能\n\n| Sparkのリソース名                  | 利用可能な機能                 | 説明                                 |\n| :-------------------------------- | :---------------------------- | :---------------------------------- |\n| Person/People                     | List/Get                      | 利用可能。Get Meも利用可能                     |\n| Space(Room)                       | List/Create/Get/Update/Delete | 利用可能。Roomは、API Clientでは、Spaceと呼ばれる。                                  |\n| SpaceMembership(Membership)       | List/Create/Get/Update/Delete | 利用可能。Membershipは、API Clientでは、SpaceMembershipと呼ばれる。        |\n| Message                           | List/Create/Get/Delete        | 利用可能。ローカルのstreamからファイル添付も可能 |\n| Team                              | List/Create/Get/Update/Delete | 利用可能。                                   |\n| TeamMembership                    | List/Create/Get/Update/Delete | 利用可能。                                   |\n| Webhook                           | List/Create/Get/Update/Delete | 利用可能。                                   |\n| File                              | GetInfo/GetData/Upload        | 利用可能。                                   |\n\n### Admin機能\n| Sparkのリソース名 | 利用可能な機能                | 説明 |\n| :-------------- | :---------------------------- | :---------------------------------------------- |\n| Person/People   | Create/Update/Delete          | 利用可能。                                       |\n| Event           | List/Get                      | 利用可能。                                       |\n| Organization    | List/Get                      | 利用可能。                                       |\n| License         | List/Get                      | 利用可能。                                       |\n| Role            | List/Get                      | 利用可能。                                       |\n\n### ストレージのTokenの暗号化と復号\n\n`ProtectedString`が、Token保存時の暗号化と、読み込み時の復号の機能を提供します。  \n詳細は後述。\n\n### Pagination機能\n\nCisco Spark APIのpaginationに関しては、[ここ](https://developer.ciscospark.com/pagination.html)を参照。\n\n`result.HasNext`と`result.ListNextAsync()`が、Cisco Spark API Clientで利用可能です。  \n詳細は後述。\n\n### Retry-Afterの取得\n\n`result.HasRetryAfter`と `result.RetryAfter`が、Cisco Spark API Clientで利用可能です。  \nまた、 `RetryExecutor`が利用可能です。  \n詳細は後述。\n\n### HTTP Statusコードの取得\n\n`result.HttpStatusCode`が、Cisco Spark API Clientで利用可能です。  \n詳細は後述。\n\n### エラーコードと詳細の取得\n\nCisco Spark APIは、エラーコードと詳細を返す場合があります。  \n`result.Data.HasErrors`と`result.Data.GetErrors()`が、Cisco Spark API Clientで利用可能です。\n\n### 部分エラーの取得\n\nCisco Spark APIは、部分的なエラーを返す場合があります。  \n部分エラーの詳細に関しては、[ここ](https://developer.ciscospark.com/errors.html)を参照。  \n`Item.HasErrors`と`Item.GetPartialErrors()`が、Cisco Spark API Clientで利用可能です。\n\n### trackingIdの取得\n\ntrackingIdは、Cisco Spark APIのテクニカルサポートで利用される可能性があります。\n\n`result.TrackingId`が、Cisco Spark API Clientで利用可能です。  \n詳細は後述。\n\n### Validates webhook secret\n\n`Webhook.CreateEventValidator()`が、Cisco Spark API Clientで利用可能です。   \n詳細は後述。\n\nCisco Spark API Clientの`CreateWebhookAsync()`メソッドはデフォルトでは、webhook secretを動的に生成します。\n\n### Webhook listener(.NET Standard 2.0+, .NET Core 2.0+, .NET Framework 4.5.2+)\n\nWebhook listener機能は、簡易的なWebhookのサーバ機能を提供します。  \n\u003e **注記: この機能は、簡単なテスト時の利用を想定しています。  \n\u003e 運用環境等では、より信頼性のあるサーバをご利用ください。**\n\n`WebhookListener`が、Cisco Spark API Clientで利用可能です。   \n詳細は後述。\n\n---\n## 基本的な使い方\n\n### Cisco Spark API Clientのインストール\n\n`Cisco Spark API Client`は、以下のいずれかの方法で、`NuGet` package manager経由で入手できます。\n\n* NuGet Package ManagerのGUI  \n\"`Thrzn41.CiscoSpark`\"を検索してインストール。\n\n* NuGet Package ManagerのCLI  \n```\nPM\u003e Install-Package Thrzn41.CiscoSpark\n```\n\n* .NET Client  \n```\n\u003e dotnet add package Thrzn41.CiscoSpark\n```\n\n### Cisco Spark API Client関連のusingディレクティブ\n\nusingディレクティブを利用する場合は、以下の名前空間を指定します。\n\n``` csharp\nusing Thrzn41.Util\nusing Thrzn41.CiscoSpark\nusing Thrzn41.CiscoSpark.Version1\n```\n\n必要に応じて、`Thrzn41.CiscoSpark.Version1.Admin`も利用可能です。\n\n### Cisco Spark API Clientインスタンスの作成\n\nCisco Spark API Clientのインスタンスは可能な限り長い期間使いまわすようにします。\n\n``` csharp\n/// 基本API利用時。\nSparkAPIClient spark = SparkAPI.CreateVersion1Client(token);\n```\n\nAdmin APIを利用する場合は、Admin API用のインスタンスを作成する必要があります。  \n`SparkAdminAPIClient`は、`SparkAPIClient`の全機能に加えて、Adminの機能が利用できます。  \n``` csharp\n/// Admin API利用時。\nSparkAdminAPIClient spark = SparkAPI.CreateVersion1AdminClient(token);\n```\n\n\u003e **注記: 'token'は、Cisco Spark APIでは、非常にセンシティブな情報です。  \n\u003e 'token'は、慎重に保護する必要があります。  \n\u003e ソースコード中に直接記載したり、安全ではない方法で保存しないようにします。  \n\u003e `Cisco Spark API Client`では、トークンを暗号化したり復号する方法を、いくつか提供しています。  \n\u003e 独自の方法で暗号化や復号、保護を実装する場合は、インスタンス作成時に、復号されたトークン文字列を利用することができます。**\n\n### 暗号化したTokenをストレージに保存する\n\n``` csharp\nchar[] tokens = GetBotTokenFromBotOwner();\n\nvar protectedToken = LocalProtectedString.FromChars(tokens);\nLocalProtectedString.ClearChars(tokens);\n\nSave(\"token.dat\",   protectedToken.EncryptedData);\nSave(\"entropy.dat\", protectedToken.Entropy);\n```\n\n**注記: LocalProtectedStringはメモリ内での保護は提供していません。  \nTokenを保存する際の、暗号化と復号での利用を想定しています。**\n\n\n### 暗号化したTokenをストレージから読み込んで、Cisco Spark API Clientのインスタンスを作成する\n\n``` csharp\nbyte[] encryptedData = Load(\"token.dat\");\nbyte[] entropy       = Load(\"entropy.dat\");\n\nvar protectedToken = LocalProtectedString.FromEncryptedData(encryptedData, entropy);\n\n/// 基本API利用時。\nSparkAPIClient spark = SparkAPI.CreateVersion1Client(protectedToken);\n```\n\n\u003e **注記: オプションに応じて、暗号化されたデータは、暗号化したときと同じローカルユーザまたはローカルマシン上でのみ復号できます。**\n\n### Cisco Sparkのスペースにメッセージを投稿する\n\n``` csharp\nvar result = await spark.CreateMessageAsync(\"xyz_space_id\", \"こんにちは, Spark!\");\n\nif(result.IsSuccessStatus)\n{\n   Console.WriteLine(\"メッセージが投稿されました: id = {0}\", result.Data.Id);\n}\n```\n\n### 成功, 失敗, エラーのハンドリング\n\n`result.IsSuccessStatus`を使って成功したかどうか確認できます。  \n`result.Data.HasErrors`や`result.Data.GetErrorMessage()`を使ってCisco Spark APIサービスからエラーコード、エラーメッセージを受け取ることができます。\n\n``` csharp\nvar result = await spark.CreateMessageAsync(\"xyz_space_id\", \"こんにちは, Spark!\");\n\nif(result.IsSuccessStatus)\n{\n   Console.WriteLine(\"メッセージが投稿されました: id = {0}\", result.Data.Id);\n}\nelse\n{\n  Console.WriteLine(\"メッセージの投稿に失敗しました: status = {0}, trackingId = {1}\", result.HttpStatusCode, result.TrackingId);\n\n  if(result.Data.HasErrors)\n  {\n    Console.WriteLine( result.Data.GetErrorMessage() );\n  }\n}\n```\n\n例外を捕捉したい場合は、データ取得時に、`result.GetData()`を利用することができます。  \n`result.GetData()`は、リクエスト失敗時に`SparkResultException`をスローします。  \n(一方、`result.Data`は、`SparkResultException`を送出しません。)\n\n``` csharp\ntry\n{\n  var result = await spark.CreateMessageAsync(\"xyz_space_id\", \"こんにちは, Spark!\");\n\n  var message = result.GetData();\n\n  Console.WriteLine(\"メッセージが投稿されました: id = {0}\", message.Id);\n}\ncatch(SparkResultException sre)\n{\n  Console.WriteLine(\"メッセージの投稿に失敗しました: status = {0}, trackingId = {1}, description = {2}\",\n                      sre.HttpStatusCode, sre.TrackingId, sre.Message);\n}\n```\n\n### Cisco Sparkのスペースに添付ファイル付きでメッセージを投稿する\n\n``` csharp\nusing (var fs   = new FileStream(\"path/myfile.png\", FileMode.Open, FileAccess.Read, FileShare.Read))\nusing (var data = new SparkFileData(fs, \"imagefile.png\", SparkMediaType.ImagePNG))\n{\n    var result = await spark.CreateMessageAsync(\"xyz_space_id\", \"添付ファイル付き\", data);\n\n    if(result.IsSuccessStatus)\n    {\n       Console.WriteLine(\"添付ファイル付きでメッセージが投稿されました: id = {0}\", result.Data.Id);\n    }\n}\n```\n\n### Cisco Sparkの1:1スペースにメッセージを投稿する\n\n``` csharp\nvar result = await spark.CreateDirectMessageAsync(\"targetuser@example.com\", \"こんにちは, Spark!\");\n\nif(result.IsSuccessStatus)\n{\n   Console.WriteLine(\"メッセージが投稿されました: id = {0}\", result.Data.Id);\n}\n```\n\n### スペースの一覧を取得する\n\n``` csharp\nvar result = await spark.ListSpacesAsync();\n\nif(result.IsSuccessStatus \u0026\u0026 result.Data.HasItems)\n{\n  foreach (var item in result.Data.Items) {\n    Console.WriteLine(\"Space: title = {0}\", item.Title);\n  }  \n}\n```\n\n### ファイルの情報やデータを取得する\n\nダウンロードせずにファイルの情報だけ入手する。\n\n``` csharp\nvar result = await spark.GetFileInfoAsync(new Uri(\"https://api.example.com/path/to/file.png\"));\n\nif(result.IsSuccessStatus)\n{\n  var file = result.Data;\n\n  Console.WriteLine(\"File: Name = {0}, Size = {1}, Type = {2}\", file.Name, file.Size?.Value, file.MediaType?.Name);\n}\n```\n\nファイルをダウンロードする。\n``` csharp\nvar result = await spark.GetFileDataAsync(new Uri(\"https://api.example.com/path/to/file.png\"));\n\nif(result.IsSuccessStatus)\n{\n  var file = result.Data;\n\n  Console.WriteLine(\"File: Name = {0}, Size = {1}, Type = {2}\", file.Name, file.Size?.Value, file.MediaType?.Name);\n\n  using(var stream = file.Stream)\n  {\n    // streamにファイルのデータが含まれる。\n  }\n}\n```\n\n### Pagenation機能を利用する\n\n``` csharp\nvar result = await spark.ListSpacesAsync();\n\nif(result.IsSuccessStatus)\n{\n  //\n  // ここで何か処理する...\n  //\n\n  if(result.HasNext)\n  {\n    // 続きのリストがあれば取得する。\n    result = await result.ListNextAsync();\n\n    if(result.IsSuccessStatus)\n    {\n      // ...\n    }\n  }\n}\n```\n\n### Http status codeを取得する\n\n``` csharp\nvar result = await spark.ListSpacesAsync();\n\nConsole.WriteLine(\"Status is {0}\", result.HttpStatusCode);\n```\n\n### Retry afterを取得する.\n\n``` csharp\nvar result = await spark.ListSpacesAsync();\n\nif(result.IsSuccessStatus)\n{\n  //\n  // ここで何かする...\n  //\n}\nelse if(result.HasRetryAfter)\n{\n  Console.WriteLine(\"{0}後にリトライしなきゃ!!\", result.RetryAfter.Delta);  \n}\n```\n\n### Retry Executor\n\n`RetryExecutor`を利用してリトライ処理を容易にします。\n\n``` csharp\n// RetryExecutor.Oneは最大で1回のリトライを試みます。\nvar result = RetryExecutor.One.ListAsync(\n  () =\u003e\n  {\n      // このメソッドは必要に応じて、リトライされます。\n      return spark.ListSpacesAsync();\n  },\n\n  (r, retryCount) =\u003e\n  {\n      // ここは、リトライが実行される前に呼び出されます。\n\n      // ここでリトライ時のログの出力等の処理が可能です。\n      Log.Info(\"Retry is required: delta = {0}, counter = {1}\", r.RetryAfter.Delta, retryCount);\n\n      // 'true'を返すとリトライが実行されます。\n      return true;\n  }\n);\n```\n\n### TrackingIdを取得する\n\n``` csharp\nvar result = await spark.ListSpacesAsync();\n\nConsole.WriteLine(\"Tracking id: {0}\", result.TrackingId);  \n```\n\n### Markdown Builder.\n\n``` csharp\nvar md = new MarkdownBuilder();\n\n// メンションと番号付きリストでMarkdownを作成。\nmd.Append(\"こんにちは、\").AppendMentionToPerson(\"xyz_person_id\", \"〇〇さん\").AppendLine();\nmd.AppendOrderedList(\"Item1\");\nmd.AppendOrderedList(\"Item2\");\nmd.AppendOrderedList(\"Item3\");\n\nvar result = await spark.CreateMessageAsync(\"xyz_space_id\", md.ToString());\n```\n\n### Webhookに通知されたデータを検証する\n\n``` csharp\nvar webhook = await spark.GetWebhookAsync(\"xyz_webhook_id\");\n\nvar validator = webhook.CreateEventValidator();\n```\n\nイベントがWebhookのURIに通知された際には、\nx-Spark-Signatureがハッシュ値を持っています。\n\nvalidatorを利用して、データの整合性を確認できます。\n\n``` csharp\nbyte[] webhookEventData = GetWebhookEventData();\n\nif( validator.Validate(webhookEventData, \"xyz_x_spark_signature_value\") )\n{\n  Console.WriteLine(\"通知されたイベントデータの検証に成功!\");\n}\n```\n\n### Webhookの通知管理\n\nWebhook notification managerを使ってWebhookへの通知を管理します。\n\n\n* インスタンスを作成する。\n\n``` csharp\nvar notificationManager = new WebhookNotificationManager();\n```\n\n* 通知用のfunctionを登録します。\n\n``` csharp\nvar webhook = await spark.GetWebhookAsync(\"xyz_webhook_id\");\n\nnotificationManager.AddNotification(\n  webhook,\n  (eventData) =\u003e\n  {\n    Console.WriteLine(\"イベントを受信, id = {0}\", eventData.Id);\n  }\n);\n```\n\n* イベントの受信時。\n\n``` csharp\nbyte[] webhookEventData = GetWebhookEventData();\n\n// Signatureが確認され登録したfunctionにイベントデータが通知されます。\nnotificationManager.ValidateAndNotify(webhookEventData, \"xyz_x_spark_signature_value\", encodingOfData);\n```\n\n### Webhook Listener\n\n* Webhook listenerのインスタンスの作成。\n\n``` csharp\nvar listener = new WebhookListener();\n```\n\n* 待ち受けする、ホストとポートを登録する。\n\n待ち受けには、TLS/httpsを利用すべきです。  \nそのためには、まず、実行環境側で`netsh`ツールなどを利用して、有効な証明書をバインドしておく必要があります。\n\nバインドされたアドレスとポート番号で、エンドポイントを追加します。\n\n``` csharp\nvar endpointUri = listener.AddListenerEndpoint(\"yourwebhookserver.example.com\", 8443);\n```\n\n* Webhook listener用のWebhookを作成します。\n\n`listener.AddListenerEndpoint()`が返す`endpointUri`がWebhookの通知先Uriになります。  \n\n``` csharp\nvar result = await spark.CreateWebhookAsync(\n  \"my webhook for test\",\n  endpointUri,\n  EventResource.Message,\n  EventType.Created);\n```\n\n* Webhook listenerにWebhookと通知先funcを登録します。\n\n``` csharp\nvar webhook = result.Data;\n\nlistener.AddNotification(\n  webhook,\n  async (eventData) =\u003e\n  {\n    Console.WriteLine(\"Eventが通知されました, id = {0}\", eventData.Id);\n\n    if(eventData.Resouce == EventResouce.Message)\n    {\n      Console.WriteLine(\"Message, id = {0}\", eventData.MessageData.Id);\n    }\n  }\n);\n```\n\n* Listenerの開始。\n\nListenerを開始すると、イベント発生時に登録したfunctionに通知されます。\n\n``` csharp\nlistener.Start();\n```\n\n### Webhook listenerをngrokと共に利用する\n\nグローバルIPアドレスが利用できない場合、\n[ngrok](https://ngrok.com/)などのトンネリングサービスが便利な場合があります。\n\n* ngrokの入手と起動。\n\nngrokのコマンドラインツールは、[ここから入手](https://ngrok.com/)できます。\n\n以下のコマンドで、トンネリングサービスを起動して、localhostの8080ポートにフォワードされます。\n\n```\nprompt\u003e ngrok http 8080 --bind-tls=true\n```\n\n* Webhook listenerのインスタンスの作成。\n\n``` csharp\nvar listener = new WebhookListener();\n```\n\n* 待ち受けする、ホストとポートを登録する。\n\nngrokは、`localhost`へフォワードします。\n\n``` csharp\nvar endpointUri = listener.AddListenerEndpoint(\"localhost\", 8080, false);\n```\n\n* Webhook listener用のWebhookを作成します。\n\nこの例では、ngronのトンネリングサービスを利用しています。  \n`listener.AddListenerEndpoint()`が返したUriは、フォワード先のUriです。\n\nWebhookには、ngrok側のUriを指定する必要があります。\n\nngrokが、`https://ngrok-xyz.example.com`を割り当てた場合、  \n`String.Format(\"https://ngrok-xyz.example.com{0}\", endpointUri.AbsolutePath)`をWebhookの宛先として指定します。\n\n``` csharp\nvar result = await spark.CreateWebhookAsync(\n  \"テスト用のWebhook\",\n  new Uri(String.Format(\"https://ngrok-xyz.example.com{0}\", endpointUri.AbsolutePath)),\n  EventResource.Message,\n  EventType.Created);\n```\n\n* Webhook listenerにWebhookと通知先funcを登録します。\n\n``` csharp\nvar webhook = result.Data;\n\nlistener.AddNotification(\n  webhook,\n  async (eventData) =\u003e\n  {\n    Console.WriteLine(\"Eventが通知されました, id = {0}\", eventData.Id);\n\n    if(eventData.Resouce == EventResouce.Message)\n    {\n      Console.WriteLine(\"Message, id = {0}\", eventData.MessageData.Id);\n    }\n  }\n);\n```\n\n* Listenerの開始。\n\nListenerを開始すると、イベント発生時に登録したfunctionに通知されます。\n\n``` csharp\nlistener.Start();\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fthrzn41%2Fciscosparkapiclient","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fthrzn41%2Fciscosparkapiclient","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fthrzn41%2Fciscosparkapiclient/lists"}