Bot Framework で Slack bot 構築メモ

次の 2 つの記事をもとに、Microsoft Bot Framework および Microsoft Cognitive Services を利用した表情分析 bot API を公開して、Slack をクライアントとして使えるように構成してみました。

- [人工知能パーツ Microsoft Cognitive Services を使った表情分析アプリを作ろう！ (Emotion API × Bot Framework 編)](http://qiita.com/annie/items/c9a8b83234dd869912c9)
- [Azure で Web 公開＆お手軽 Web Chat を試す](http://qiita.com/annie/items/590fa480719ed93998df)

Slack bot の構成方法については、[Bot Framework サイト](https://dev.botframework.com/)で Slack の設定を追加するときに表示される手順の通りに進めればできました。

以下では、構築時の作業メモや注意点について簡単に書いておきます。

#### (1) 開発環境における実行について
ローカルの開発環境においては、Visual Studio で開始した bot API に Bot Framework Channel Emulator から接続することになりますが、[デバッグなしで開始 (Ctrl+F5)] では 401 エラーとなります。
[デバッグの開始 (F5)] で実行すれば接続できます。

これは、Controller に [BotAuthentication](https://github.com/Microsoft/BotBuilder/blob/master/CSharp/Library/Microsoft.Bot.Connector/BotAuthentication.cs) 属性が付けられていることによって、デバッガーにアタッチされているときは認証不要になるためです。
したがって、開発環境では [デバッグの開始 (F5)] で実行しましょう。

#### (2) アカウント情報の設定について
Azure に公開された API の Web.config の appSettings にアカウント情報を設定するには、Azure Portal の [アプリケーション設定] を利用するとよいでしょう。

#### (3) クライアント上でのテキストの表示について
[テキストは既定で Markdown 形式](https://docs.botframework.com/en-us/csharp/builder/sdkreference/activities.html)です。したがって、記号などを使用するときは注意が必要です。
なお、reply.TextFormat を plain に設定しても、Web Chat では無効のようです。
また、reply.TextFormat を plain に設定しても、Slack ではなぜか * が _ と表示されます。

#### (4) Activity.Type について
Slack からは、activity.Type が ActivityTypes.Message でない要求が送られてくることがあります。
activity.Type の値により処理を分岐させないと、応答メッセージが重複して表示されてしまいます。

#### (5) URL の送信について
Slack から URL を送信すると、bot 側で受信するテキストでは、URL が < と > で括られます。

上記の (4), (5) を踏まえて改良したものが次のソースコードです。
全体のソースコードは、[EmotionBotApi (GitHub)](https://github.com/sakapon/Samples-2016/tree/master/BotSample/EmotionBotApi) にあります。

```C#:MessagesController.cs
public async Task<HttpResponseMessage> Post([FromBody]Activity activity)
{
    if (activity.Type != ActivityTypes.Message)
        return Request.CreateResponse(HttpStatusCode.OK);
    if (string.IsNullOrWhiteSpace(activity.Text))
        return Request.CreateResponse(HttpStatusCode.OK);

    var connector = new ConnectorClient(new Uri(activity.ServiceUrl));
    // In Slack, URLs are qualified by '<' and '>'.
    var text = activity.Text.Trim('<', '>');

    var echoMessage = $"You sent this picture.  \n![]({text})";
    await Reply(connector, activity, echoMessage);

    var mainMessage = await GetEmotionsAsync(text);
    await Reply(connector, activity, mainMessage);

    return Request.CreateResponse(HttpStatusCode.OK);
}

static async Task<string> GetEmotionsAsync(string text)
{
    if (!Uri.IsWellFormedUriString(text, UriKind.Absolute)) return "Send the URL of a picture.";

    var emotions = await RecognizeEmotionsAsync(text);
    if (emotions == null) return "The emotions recognition failed.";
    if (emotions.Length == 0) return "Nobody is recognized.";

    var topScore = emotions[0].Scores
        .ToRankedList()
        .First();
    return $"{topScore.Key}: {topScore.Value:P1}";
}

static async Task<Emotion[]> RecognizeEmotionsAsync(string imageUrl)
{
    var client = new EmotionServiceClient(SubscriptionKey);

    try
    {
        return await client.RecognizeAsync(imageUrl);
    }
    catch (Exception)
    {
        return null;
    }
}

static async Task Reply(ConnectorClient connector, Activity activity, string message)
{
    var reply = activity.CreateReply(message);
    await connector.Conversations.ReplyToActivityAsync(reply);
}
```


次のように、HTTP ステータス コード 202 でメッセージを返します。

```C#
await connector.Conversations.ReplyToActivityAsync(reply);
```

完了したら、HTTP ステータス コード 200 を返します。

```C#
return Request.CreateResponse(HttpStatusCode.OK);
```

Slack で実行したときのスクリーン ショットです。
![Emotion Bot - Slack](https://qiita-image-store.s3.amazonaws.com/0/9649/b91d2a4a-726f-0fd4-ccea-5de08ff05ded.jpeg)

素因数分解 bot については、[Bot Framework で Slack の bot を構成する](https://sakapon.wordpress.com/2016/11/05/bot-framework-slack/)に書きました。

**作成したサンプル**
[BotSample (GitHub)](https://github.com/sakapon/Samples-2016/tree/master/BotSample)

**バージョン情報**
.NET Framework 4.6
Microsoft.Bot.Builder 3.3.0
Microsoft.ProjectOxford.Emotion 1.0.331.1