C#でSlackへWebSocket接続(RTM) その2 Windows7対応

[前回](https://qiita.com/khsk/items/922d47dc23fd0197aa8f)は既存のライブラリではWindows7でWebSocketが使えないことで断念した接続処理を自作します。

WebSocket接続には`websocket-sharp`
[sta/websocket-sharp: A C# implementation of the WebSocket protocol client and server](https://github.com/sta/websocket-sharp)
を使います。これならWindows7でも動作します。

Slackは直接ws接続できるわけではなく、まずはトークンを利用したhttp通信でws用のURLを払いだしてもらう必要があります。
なので、wsクライアントだけではなくhttpクライアントも書く必要があります。

C#にはHTTP用クラスがいくつかデフォルトで用意されているのですが、今回はアセンブリが見つからず`HttpClient`が使えなかったことと、同期的でいいので`WebClient`を利用しました。

JSONは標準ライブラリで解析する方法がやや長そうで読めなかったので、前回のSlackAPIを動かすために必要で用意していた`Json.NET`を使っちゃいます。

```cs
using System.Threading;
using System.Threading.Tasks;
using System;

using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

using System.Text;

using System.Collections.Generic;

using System.Net;
using WebSocketSharp;

class AuthedResult {
    public string url { get; set;}
}

class ReceivedResult {
    public string type { get; set;}
    public string subtype { get; set;}
    public string text { get; set;}



    public JArray attachments { get; set;}
    public string channel { get; set;}
    public string user { get; set;}
    public string username { get; set;}
}


class Slack {
    static void Main() {
        try {
            string url = GetWsUrl();
            using (var ws = new WebSocket (url)) {
            bool ConnectedFlag = false;

            ws.OnMessage += (sender, e) => {
                if (!ConnectedFlag) {
                    // おそらくOnMessageイベントでやる処理ではないが、メッセージがきちゃうので弾きついでに。
                    ConnectedFlag = true;
                    if (e.Data !="{\"type\": \"hello\"}") {
                        throw new Exception("ws connection failed: " + e.Data);
                    }
                    Console.WriteLine ("ws connection success");
                    return;
                }

                Console.WriteLine ("OnMessage: " + e.Data);

                ReceivedResult json = JsonConvert.DeserializeObject<ReceivedResult>(e.Data);
                Console.WriteLine ("OnMessage json: " + json.text);
                if (json.type == "error") {
                    throw new Exception("received type:error: " + e.Data);
                }

                // LINQ覚えたら書き直すかも ただ用途によっては適さない気もする

                if (json.attachments == null) {
                    return;
                }

                foreach( JObject attachment in json.attachments) {
                    JArray fields = (JArray)attachment["fields"];
                    foreach (JObject field in fields) {
                        JValue title = (JValue) field["title"];
                        JValue value = (JValue) field["value"];
                        Console.WriteLine(title.Value);
                        Console.WriteLine(value.Value);
                    }
                }
            };
            ws.Connect ();

            Console.ReadKey (true);
            }
        } catch (Exception e) {
            Console.WriteLine (e.Data);

        }
    }

    static string GetWsUrl() {
        using (WebClient client = new WebClient ()) {
            client.QueryString.Add("token", "xoxp-hogehoge");
            string sresult = client.DownloadString("https://slack.com/api/rtm.connect");
            AuthedResult json = JsonConvert.DeserializeObject<AuthedResult>(sresult);

            //Console.WriteLine(sresult);
            //Console.WriteLine(json.url);
            //Console.ReadKey (true);
            return json.url;
        }

    }

}

```

参考を見るにJSONをデシリアライズするには構造体を宣言しなければならないと思い込み最初に少し作ったんですが、構造を暗黙的に知っている前提で`JObject`だけで処理してもＯＫです。
もちろんちゃんと宣言したほうが親切ですが。

RTMではユーザー名やチャンネル名はIDで来るので、そのままでは扱いづらいです。

これらを表示名に変換するには事前に`channels.list`や`users.list`で事前に対応表を作っておく必要があるかもしれません。

[API Methods | Slack](https://api.slack.com/methods)

```:bat
@echo off
C:\Windows\Microsoft.NET\Framework\v4.0.30319\csc.exe ^
 /r:"Newtonsoft.Json.dll" ^
 /r:"websocket-sharp.dll" ^
  ws.cs
```

# 参考

[Real Time Messaging API | Slack](https://api.slack.com/rtm)

[SlackのRTM APIを直接呼ぶときに困ったこと - Qiita](https://qiita.com/s-suefusa/items/58d1934f645ddb434eec)

[Visual Studio が無い Windows 環境で c# コードをコンパイルする - Qiita](https://qiita.com/asterisk9101/items/4f0e8da6d1e2e9df8e14)

[sta/websocket-sharp: A C# implementation of the WebSocket protocol client and server](https://github.com/sta/websocket-sharp)

[外部リソースの解放には using ステートメントを使う - Qiita](https://qiita.com/Nossa/items/947f8985e4d5f13d9bc2)

[Json.NET - Newtonsoft](https://www.newtonsoft.com/json)

[Json.NET (Newtonsoft.Json) の基本的な使い方 | @Subaru](https://netweblog.wordpress.com/2016/10/24/json-net-newtonsoft-json-usage/)

[C#/JSON: Newtonsoft.Json の最も簡単な使い方 - C++ ときどき ごはん、わりとてぃーぶれいく☆](https://usagi.hatenablog.jp/entry/2018/12/12/103435)

[Deserialize an Object](https://www.newtonsoft.com/json/help/html/DeserializeObject.htm)

[C#でWebAPI叩いてJson形式の結果を受け取る処理メモ - Qiita](https://qiita.com/ikejpcw/items/d8b4790195fe496c6e8f)


[C# HTTPクライアントまとめ - Qiita](https://qiita.com/aosho235/items/1ad9ee05b1be62ca2c59)