【THETAプラグイン】THETA から手軽に Slack にアップロード

# 【THETAプラグイン】THETA から手軽に Slack にアップロード

## はじめに
はじめまして、リコーの@yomura_です。

RICOH THETA で撮った写真をもっと気軽に共有できたら良いなと思い、
THETA Vで撮った写真をその場で Slack にアップロードする機能を実装してみました。

Slackはメッセージ送信などに関する多くのAPIを公開しています。
今回は画像ファイルをアップロードすることが目的なので、
こちらの[files.upload](https://api.slack.com/methods/files.upload)のAPIを叩いてみます。

画像ファイルがアップロードされるとSlackbotが画像を指定のチャネルに投稿してくれます。


![system.png](https://qiita-image-store.s3.amazonaws.com/0/381074/8c15ab71-dc59-451f-9b09-e7b72b6db864.png)

## RICOH THETAプラグインについて
THETAプラグインをご存じない方は[こちら](https://api.ricoh/products/theta-plugin/)をご覧ください。
興味を持たれた方は[Twitter](https://twitter.com/thetaplugin)のフォローと [THETAプラグイン開発コミュニティ(Slack)](https://join.slack.com/t/thetadeveloper/shared_invite/enQtNzcxODI2MTk2ODg0LWY5YmU2ZTIxNGYwNzlmNWZjMWMyMWFlMzY3ZmQ3NGU3ZjZiZjI0Njk4MGIxMDU5NDJiMzQ4Y2FkNDEzYmFlNGU)への参加もよろしくお願いします。


## Slackに投稿するまでの流れ

事前準備が必要ですが、3ステップで簡単に投稿できます。

1. 無線ボタンを押してCLモードにする (※1)
2. MODEボタンを長押ししてプラグインを起動する
3. シャッターボタンを押す → 指定のチャンネルに画像を投稿！

※1 : CLモードの設定についてはマニュアルや動画で詳しく解説されています。

* マニュアル: https://support.theta360.com/ja/manual/v/content/prepare/prepare_08.html
* 動画: https://www.youtube.com/watch?v=jE9_VqaVuLM&list=PLkb48xwXKzen3Ksjc-aXlHhCneLlRcwrK


## 実装について

公式の SDK が提供されているので、こちらをベースにして Slack API を叩く部分だけ追加しました。
[GitHub - ricohapi/theta-plugin-sdk: RICOH THETA Plug-in SDK](https://github.com/ricohapi/theta-plugin-sdk)

このプラグインのサンプルソースコードは [GitHub](https://github.com/theta-skunkworks/theta-plugin-slack-uploader-sample) で公開しています。



### リクエストの形式
今回は HTTPクライアントライブラリ OkHttp3 を利用してPOSTリクエストを送信しました。

app の build.gradle に一行追加してビルドすると利用できるようになります。(執筆時点での最新バージョン 3.13.1)

```gradle:build.gradle
dependencies {
    ...
    ...
    implementation 'com.squareup.okhttp3:okhttp:3.13.1'
}
```

リクエストの形式は `multipart/form-data` で送信しました。
`multipart/form-data` 形式のリクエストは OkHttp の MultipartBody クラスを使います。
送信データの境界となる文字列は現在時刻から生成しました。

```java
final String boundary = String.valueOf(System.currentTimeMillis());

RequestBody requestBody = new MultipartBody.Builder(boundary)
                              .setType(MultipartBody.FORM)
                              ....
```


### リクエストの引数
今回は以下の引数にデータを入れました。

| 引数名 | 説明 |
|:--|:--|
| file | 画像ファイル |
| token | 画像を投稿してもらうSlackbotのトークン |
| channels |Slackのチャネル名 |
| filename | 画像のファイル名 |
| filetype | 画像のデータ形式 |
| initial_comment | Slackbotが投稿する時のコメント |
| title | 画像のタイトル |

#### ファイルの取得

THETAで撮影した画像は外部ストレージに保存されているので、以下のようにDCIMのディレクトリを取得する必要があります。

```java
final String dcim = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM).getPath();
File file = new File(dcim + fileName);
```

#### チャネル名の取得

Slackアプリでチャネル名を右クリックして「リンクを取得」すると以下のような文字列がコピーされます。この messages/ 以降の文字列がチャネル名です。

```
https://{ワークスペース名}.slack.com/messages/{チャネル名}
```


#### 一連の流れ

まとめるとリクエスト送信部分の処理はこのようになりました。

```java:UploadTask.java
private String postToSlackbot() {

    //  SlackAPIのURL
    final String apiUrl = "https://slack.com/api/files.upload";
    // Slackの投稿先チャネル
    final String slackChannel = "write your channel ID here"
    // Slackbotのトークン
    final String slackBotToken = "write your slackbot token here"

    // ファイル取得
    final String dcim = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM).getPath();
    File file = new File(dcim + this.fileName);

    // HTTPクライアント
    OkHttpClient client = new OkHttpClient.Builder()
            .connectTimeout(30, TimeUnit.SECONDS)
            .writeTimeout(60, TimeUnit.SECONDS)
            .readTimeout(60, TimeUnit.SECONDS)
            .build();

    // リクエストボディ
    final String timestamp = getDateString();
    final String boundary = String.valueOf(System.currentTimeMillis());
    RequestBody requestBody = new MultipartBody.Builder(boundary)
            .setType(MultipartBody.FORM)
            .addFormDataPart(
                    "file",
                    fileName,
                    RequestBody.create(MediaType.parse("image/jpeg"), file)
            )
            .addFormDataPart("token", slackBotToken)
            .addFormDataPart("channels", slackChannel)
            .addFormDataPart("filename", "IMG_" + timestamp + ".jpg")
            .addFormDataPart("filetype", "jpg")
            .addFormDataPart("initial_comment", "THETA V SlackUploaderから画像がアップロードされました。")
            .addFormDataPart("title", "IMG_" + timestamp + ".jpg")
            .build();

    // リクエスト
    Request request = new Request.Builder()
            .url(apiUrl)
            .post(requestBody)
            .build();

    Call call = client.newCall(request);
    String result = null;

    try {
        // レスポンス取得
        Response response = call.execute();
        ResponseBody body = response.body();
        if (body != null) {
            result = body.string();
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
    return result;
}
```

あとは SDK にあらかじめ用意されている TakePictureTask.java を参考に、非同期処理のためのタスククラスを作り、先ほどの処理をdoInBackgroundから呼び出します。

```java:UploadTask.java
@Override
protected String doInBackground(Void... params) {
    String result = postToSlackbot();
    return result;
}
```

### ボタンへの割り当て

- 今回はカメラボタンを「撮影 & Slackにアップロード」、モード切替ボタンを「チャネル切り替え」に割り当てました。


```java:MainActivity.java
@Override
public void onKeyDown(int keyCode, KeyEvent event) {
    if (keyCode == KeyReceiver.KEYCODE_CAMERA) {
        new TakePictureTask(mTakePictureTaskCallback).execute();  // 撮影&アップロード
    } else if (keyCode == KeyReceiver.KEYCODE_MEDIA_RECORD) {
        changeDestination();  // チャネル切り替え
    }
}
```

切り替えたときに今の投稿先がどのチャネルかわかるように音声読み上げの機能もつけようと試みましたが、THETA Vに内蔵のText-to-speechエンジンに日本語を喋らせることができなかったので断念しました。

THETAをしゃべらせる方法ついてはこちらの記事に詳しく紹介されています。
[THETA をおしゃべりにする - Qiita](https://qiita.com/shrhdk_/items/7a4b98b9f7145e6e49ac)


撮影ボタンを押すとこのように画像をアップロードすることができました。(画像はダミーです)

<img width="452" alt="upload.png" src="https://qiita-image-store.s3.amazonaws.com/0/381074/76275f28-ec92-3440-a18f-363b34e959a3.png">

## おわりに

まだまだ荒削りですが、なんとかSlackに画像を投稿することができました。

Slackをプライベートで使っている方はもちろん、
仕事で使っているチームの方も、THETAで写真を撮って簡単なメモ代わりとして共有するという使い方もありかもしれません。

今はSlackbotのトークンや投稿先はハードコーディングされているので、
Web UIから設定できるようにできればと思います。

WebUIについての記事はこちらに詳細に書かれています。

[THETAプラグインのWeb UIの実装方法【THETA プラグイン開発】 - Qiita](https://qiita.com/3215/items/d156307dfadd7dc2fbf6)