前回は既存のライブラリではWindows7でWebSocketが使えないことで断念した接続処理を自作します。
WebSocket接続にはwebsocket-sharp
sta/websocket-sharp: A C# implementation of the WebSocket protocol client and server
を使います。これならWindows7でも動作します。
Slackは直接ws接続できるわけではなく、まずはトークンを利用したhttp通信でws用のURLを払いだしてもらう必要があります。
なので、wsクライアントだけではなくhttpクライアントも書く必要があります。
C#にはHTTP用クラスがいくつかデフォルトで用意されているのですが、今回はアセンブリが見つからずHttpClient
が使えなかったことと、同期的でいいのでWebClient
を利用しました。
JSONは標準ライブラリで解析する方法がやや長そうで読めなかったので、前回のSlackAPIを動かすために必要で用意していたJson.NET
を使っちゃいます。
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
だけで処理してもOKです。
もちろんちゃんと宣言したほうが親切ですが。
RTMではユーザー名やチャンネル名はIDで来るので、そのままでは扱いづらいです。
これらを表示名に変換するには事前にchannels.list
やusers.list
で事前に対応表を作っておく必要があるかもしれません。
@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
SlackのRTM APIを直接呼ぶときに困ったこと - Qiita
Visual Studio が無い Windows 環境で c# コードをコンパイルする - Qiita
sta/websocket-sharp: A C# implementation of the WebSocket protocol client and server
外部リソースの解放には using ステートメントを使う - Qiita
Json.NET (Newtonsoft.Json) の基本的な使い方 | @Subaru
C#/JSON: Newtonsoft.Json の最も簡単な使い方 - C++ ときどき ごはん、わりとてぃーぶれいく☆