はじめに
今回Javaで作っているプログラムでSlackに投稿したいことがありました。
GASでなら以前やったのでそこからコピペ等すれば簡単ですが、Javaでの投稿は未経験でした。
Javaでのやり方はネットを探すも見つけられなかったので記事にします。
(海外のサイトやGitHubなどを漁ればあるだろうとは思いますが。)
試行錯誤している最中のコードも載せていますが、"失敗談はどうでもいいから結果が知りたい"という人は最後の方まで飛ばしてください。
ちなみに、どうにか送れるようになるまで私は1日ほどかかりました。
失敗その1 - チャンネルがない
次のようなメソッドを実行してみました。
(JSONのインデントや改行は必須ではないと思います。)
private static void testSlack(){
String json = "{\n" +
" \"channel\":\"bot−test\"," +
" \"username\":\"テスト\",\n" +
" \"text\": \"テスト\",\n" +
" \"attachments\": [\n" +
" {\n" +
" \"color\": \"#89ceeb\",\n" +
" \"text\": \"テスト\"\n" +
" }\n" +
" ]\n" +
"}";
try {
URL url = new URL("https://slack.com/api/chat.postMessage?token=トークン");
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("POST");
connection.setDoOutput(true);
connection.setRequestProperty("Content-type", "application/json");
OutputStream stream = connection.getOutputStream();
PrintStream ps = new PrintStream(stream);
ps.print(json);
ps.close();
BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
String str = "";
String body;
while ((body = reader.readLine()) != null) {
str += body + "\n";
}
System.out.println(str);
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (ProtocolException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
結果は次の通り。
(見やすいよう整形しています。)
{
"ok": false,
"error": "channel_not_found",
"warning": "missing_charset"
}
なぜかチャンネルがないと言われてしまいました。
実際に存在するチャンネル名なのでそのはずがありません。
もしかしたら多分名前はNGでチャンネルIDの方を渡さないといけないのかもしれません。
GASの時はそんなことなかったのですが・・・。
charsetがないというのは警告なのでとりあえずスルーします。
失敗その2 - テキストがない
Slack APIは項目をパラメータとして渡せれば良いので、チャンネルはURLのパラメータにしてみました。
private void testSlack(){
String json = "{\n" +
" \"username\":\"テスト\",\n" +
" \"text\": \"テスト\",\n" +
" \"attachments\": [\n" +
" {\n" +
" \"color\": \"#89ceeb\",\n" +
" \"text\": \"テスト\"\n" +
" }\n" +
" ]\n" +
"}";
try {
URL url = new URL("https://slack.com/api/chat.postMessage?token=トークン&channel=%23bot-test");
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("POST");
connection.setDoOutput(true);
connection.setRequestProperty("Content-type", "application/json");
OutputStream stream = connection.getOutputStream();
PrintStream ps = new PrintStream(stream);
ps.print(json);
ps.close();
BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
String str = "";
String body;
while ((body = reader.readLine()) != null) {
str += body + "\n";
}
System.out.println(str);
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (ProtocolException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
今度はテキストがないと言われました。
{
"ok":false,
"error":"no_text",
"warning":"missing_charset"
}
そこでテキストもパラメータにと思いましたが、日本語はエンコードしないといけないので保留。
そもそもJSONのPOSTがうまくいっていないような気がします。
そもそもJavaでJSONを送信するには?
JavaでJSONを送信する方法について調べてみました。
-
【Java】【Android】【PHP】【JSON】JSON を Java から送信して、PHP で受信する - ヽ|∵|ゝ(Fantom) の 開発blog?
- 相違点:connectしている・Gson使ってる・PrintWriterで書き出している
-
AndroidのHttpURLConnectionのPOSTでハマる - 初老のボケ防止日記
- 相違点:Android用のサンプル・charsetを指定している・バイト配列にしてDataOutputStreamで書き出している
どちらも試してみたのですが、どちらもテキストがないと・・・。
(Gsonは試してません。)
cURL経由で送信する
JavaからcURLのコマンドを実行してみることにしました。
curl -XPOST --data-urlencode 'payload={"attachments":[{"color":"#89ceeb","text":"テスト"}],"text":"テスト","username":"テスト"}' https://slack.com/api/chat.postMessage?token=トークン&channel=%23bot-test
Javaのコードはこんな感じです。
private void testSlack() {
String json = "{\n" +
"\"username\":\"テスト\"," +
"\"text\": \"テスト\"," +
"\"attachments\": [" +
"{" +
"\"color\": \"#89ceeb\"," +
"\"text\": \"テスト\"" +
"}" +
"]" +
"}";
String command = "curl -XPOST --data-urlencode 'payload=" + json + "' https://slack.com/api/chat.postMessage?token=トークン&channel=%23bot-test";
try {
Process p = Runtime.getRuntime().exec(command);
p.waitFor();
InputStream input = p.getInputStream();
try (BufferedReader reader = new BufferedReader(new InputStreamReader(input))) {
String lines = "";
for (String line = reader.readLine(); line != null; line = reader.readLine()) {
lines += line + "\n";
}
System.out.println(lines);
}
} catch (InterruptedException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
その結果は次の通り。
{
"ok": false,
"error": "invalid_arg_name"
}
今度は引数の名前がおかしいみたいです。
ヘッダーをきちんと指定しても同様でした。
このコマンドをターミナルで実行するとまたチャンネルがないと言われてしまうという・・・。
どうにかcURL経由で送れるようになった
Slack APIはJSONで指定することは必須ではなく、URLのパラメータで指定することも可能です。
attachmentsをパラメータにできたらなぁと思い、中身をURLエンコードした次のようなコードを実行してみました。
ちなみにエンコード後の文字列はこちらのサイトを利用して用意しました。
private void testSlask(){
String command = "curl -XPOST https://slack.com/api/chat.postMessage?token=トークン&channel=%23bot-test&text=test&attachments=%5B%7B%22color%22%3A%22%2389ceeb%22%2C%22text%22%3A%22%E3%83%86%E3%82%B9%E3%83%88%22%7D%5D";
try {
Process p = Runtime.getRuntime().exec(command);
p.waitFor();
InputStream input = p.getInputStream();
try (BufferedReader reader = new BufferedReader(new InputStreamReader(input))) {
String lines = "";
for (String line = reader.readLine(); line != null; line = reader.readLine()) {
lines += line + "\n";
}
System.out.println(lines);
}
} catch (InterruptedException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
結果は次の通りです。
{
"channel": "チャンネルID",
"ok": true,
"message": {
"attachments": [{
"color": "89ceeb",
"text": "テスト",
"id": 1,
"fallback": "NO FALLBACK DEFINED"
}],
"subtype": "bot_message",
"text": "test",
"type": "message",
"bot_id": "BOT ID",
"username": "Slack API Tester",
"ts": "1463901412.000002"
},
"ts": "1463901412.000002"
}
ついに送れるようになりました。
cURLを使わずにJavaで投稿する
今度はエンコードをJavaで行い投稿も最初のHttpURLConnectionでの方法にしました。
private void testSlack() {
String attachments = "[" +
"{" +
"\"color\": \"#89ceeb\"," +
"\"text\": \"テスト\"" +
"}" +
"]";
try {
attachments= URLEncoder.encode(attachments,"UTF-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
try {
URL url = new URL("https://slack.com/api/chat.postMessage?token=トークン&channel=%23bot-test&text=test&attachments=" + attachments);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("POST");
connection.setDoOutput(true);
BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
String str = "";
String body;
while ((body = reader.readLine()) != null) {
str += body + "\n";
}
System.out.println(str);
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (ProtocolException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
あとがき
ずっとSlackへの投稿はJSONをPOSTすることしか考えていなかったので、トータル半日以上その線を探り続けていました。
諦めかけて他のことしてた時間を入れると1日くらいになると思います。
最後は、JSONを外部ファイル化するとか、GASのプロジェクトにPOSTしてそっちで投稿するとか、とにかく諦めて未完で結論書いて記事投稿しようとか考えたりもしました。
チャンネル・ユーザー名・テキストはパラメータ指定で行けることを知っているのでattachmentsを諦めようと思ったのですが、諦める必要はありませんでした。
URLアクセスで完結するAPIはいいですね。