山田です

山田です。山田氏とも言います。

C++ REST SDK (Casablanca) でSlack Web APIを触ってみた

はじめに

どうも、山田です。

今日は僕の所属しているクラブNITMicのAdvent Calendarということで記事を書こうと思ったんですが、技術系のブログを持っていなかったので、これを機にブログを開設しました。今後も備忘録的に使っていきたいと思っています。

さて、今日はタイトルの通りcasablanca(C++ REST SDK)を使ってSlackのAPIを触ってみて得られたなんやかんやを残しておきたいと思った次第です。

そもそも何故こんなことをしているかと言いますと、うちの部ではコミュニケーション用のツールとしてSlackを使っているんですが、お金を払っていないのでファイル容量が少なめです。なので不要なファイルを消したい、しかし、Slackにはファイルを一括で消せる機能がないそうで…

「ないんだったら、自分で作ればいいのよ!」

ということで、作ることにしました。

最初はjavascriptで書いていて「めっちゃ簡単やん!」と思ったので、どうせなら普段使っているC++でもやってみようと、調べて出てきたのがCasablancaというわけです。
 

準備

今回はどこのチャンネル、プライベートチャンネル、ダイレクトメッセージからも共有されていないファイルを一括で消そうと思います。

まずはVSのNuGetでcasablancaを調べてインストールします。すごく簡単ですね。

そしてなんやかんやを書きます。

#include <cpprest/http_client.h>
#include <cpprest/filestream.h>

using namespace web;
using namespace web::http;
using namespace web::http::client;
using namespace std;

 ここからが本番です。

とりあえずSlackのWebAPIのfiles.listメソッドを呼び出したいのですが、まずどうしたらいいのか分かりませんでした。

調べてみると、どうやらhttp_clientインスタンスを作って、requestメソッドを呼べば良いっぽい?

HTTPリクエストを送るときのオプションも設定しなければいけないみたいですが、今回はhttp_requestインスタンスを使って指定してやります。

HTTP通信には主にGETとPOSTがあって(他にもあるらしい)、GETはパラメータをURLに直接埋め込んでリクエストを送る方式で、POSTはリクエストのBodyにパラメータを含める方式です。

POSTのボディにもいくつかの表現方法があって、Content-Typeの設定を「application/x-www-form-urlencoded」にするとボディでパラメータを指定するときにURLをエンコードした形式で渡せて、「application/json」にするとJSONデータでパラメータを指定できます。

http_response filesList() {
	http_client client(L"https://slack.com/api/files.list");
	http_request request(methods::POST);
	request.headers().add(L"Content-Type", L"application/json");
	request.headers().add(L"Authorization", L"Bearer にゃーん");
	json::value postData;
	postData[L"count"] = json::value::number(200);
	request.set_body(postData);

	return client.request(request).get();
}

言い忘れていましたが、slackのAPIを使うにはトークンを発行する必要があります。発行したトークンを「にゃーん」のところに入れるわけです。

実はこれだとうまくいきません。なんでダメなんだろう、とかなり悩みましたが結局はfiles.listメソッドが「application/json」形式に対応していなかっただけでした。

「application/x-www-form-urlencoded」形式に直してみます。

http_response filesList() {
	http_client client(L"https://slack.com/api/files.list");
	http_request request(methods::POST);
	request.headers().add(L"Content-Type", L"application/x-www-form-urlencoded");
	request.set_body("token=にゃーん&count=200");

	return client.request(request).get();
}

これでオッケーです。

あとは得られた応答(JSON形式)からファイルの情報を参照し、各ファイルのchannels,groups,imsの項目を見てどこからも参照されていなければこれをfiles.deleteメソッドを使って削除します。

files.deleteメソッドはapplication/json形式が使えるので使ってみます。

void filesDelete(const json::value& jsn) {
	
  http_client client(L"https://slack.com/api/files.delete");
  http_request request(methods::POST);
  request.headers().add(L"Content-Type", L"application/json");
  request.headers().add(L"Authorization", L"Bearer にゃーん");

  for (const auto& elm : jsn.at(L"files").as_array()) {
    if (elm.at(L"channels").size() + elm.at(L"groups").size() + elm.at(L"ims").size() == 0) {
      json::value postData;
      postData[L"file"] = json::value::string(elm.at(L"id").as_string());
      request.set_body(postData);
      auto response = client.request(request).get();

      cout << response.extract_utf8string().get() << endl;
    }
  }
}

これでいけましたね。ただ、deleteしたときに「missing_charset」という警告が出ましたが、キニシナイキニシナイ。

charsetとかいうのを指定すれば警告は消えるらしいですが、特別な支障もないようなので置いておきます。


おわり

調べて出てくるサンプルはメソッドチェーンを使ってかっこいい感じに書いてあったんですが、今の僕にはまだよく理解できなかったのでそれはまたの機会にでも。

今までゲームプログラミングしかしてこなかったので新しいことができて楽しかったし、APIを触っていると大したことをしていなくてもすごいことをした気分になれるので良い感じです。
今回の経験を通して通信系とかWeb系にも興味が出てきたので、今後はそっちも触っていけたらいいなあと思います。

最後まで見ていただいてありがとうございました。