November 30, 2020 THETA Plug-in Slack

THETA Plug-ins - Uploading to Slack Easily From THETA

THETA Plug-ins - Uploading to Slack Easily From THETA

Original post (in Japanese)

Introduction

Hello, I am Yohei Omura (@yomura_) and I work at RICOH.

I wish I could share pictures taken with RICOH THETA more easily, so I implemented a function to quickly upload photos taken with a THETA V to Slack.

Slack exposes many APIs for sending messages and other actions. My purpose is to upload photos, so I used the files.upload API.

Once a photo is uploaded, Slackbot will post it to a specified channel.

slackuploader overview

About RICOH THETA Plug-ins

If you are unfamiliar with THETA Plug-ins, please see here.

If you are interested, please join the theta360.guide developer community.

Steps Up Until Post to Slack

You need to prepare in advance, but you can easily post to Slack in 3 steps.

  1. Press the Wi-Fi button to switch to CL mode
  2. Start up the plug-in by pressing and holding the mode button
  3. Press the shutter button → Post the photo to the specified channel!

CL mode settings are explained in detail in manuals and videos:

Manual: https://support.theta360.com/en/manual/v/content/prepare/prepare_08.html

Video: https://www.youtube.com/watch?v=tkqyBNOWWIY&feature=youtu.be

Implementation

Since the official SDK is available, I only added the part that connects to the Slack API.

GitHub - ricohapi/theta-plugin-sdk: RICOH THETA Plug-in SDK

Sample source code for this plugin is available on GitHub.

Request Format

I used the HTTP client library OkHttp3 to send a POST request.

You can use it by adding a line to the build.gradle of the app and building it. (Latest version 3.13.1 at the time of writing.)

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

The format of the request was sent in multipart / form-data. Requests in the form of multipart / form-data use OkHttp’s MultipartBody class. The character string that is the boundary of the transmitted data was generated from the current time.

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

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

Request Arguments

I put the data in the following arguments:

Argument Name Description
file Image file
token The Slackbot token used to have an image posted
channels Slack channel name
filename Image filename
filetype Image data format
initial_command Comments when posted by Slackbot
title Image title

Get File

Images taken with THETA are saved in external storage, so you need to get the DCIM directory as shown below.

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

Get Filename

Right-click on the channel name in the Slack app and select “Get Link” to copy the following string. The string after this message is the channel name.

https://{workspace name}.slack.com/messages/{channel name}

A Series of Flows

The sending part of processing of a request is like this.

private String postToSlackbot() {

    //  SlackAPI URL
    final String apiUrl = "https://slack.com/api/files.upload";
    // The Slack channel you're posting to
    final String slackChannel = "write your channel ID here"
    // Slackbot token
    final String slackBotToken = "write your slackbot token here"

    // get file
    final String dcim = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM).getPath();
    File file = new File(dcim + this.fileName);

    // HTTP client
    OkHttpClient client = new OkHttpClient.Builder()
            .connectTimeout(30, TimeUnit.SECONDS)
            .writeTimeout(60, TimeUnit.SECONDS)
            .readTimeout(60, TimeUnit.SECONDS)
            .build();

    // request body
    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", "Image uploaded from THETA V SlackUploader.")
            .addFormDataPart("title", "IMG_" + timestamp + ".jpg")
            .build();

    // Request
    Request request = new Request.Builder()
            .url(apiUrl)
            .post(requestBody)
            .build();

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

    try {
        // Get response
        Response response = call.execute();
        ResponseBody body = response.body();
        if (body != null) {
            result = body.string();
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
    return result;
}

After that, refer to TakePictureTask.java prepared in the SDK in advance, create a task class for asynchronous processing, and call the previous processing from doInBackground.

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

Assign to Button

I assigned the camera button to “shoot & upload to Slack” and the mode switching button to “channel switching.”

@Override
public void onKeyDown(int keyCode, KeyEvent event) {
    if (keyCode == KeyReceiver.KEYCODE_CAMERA) {
        new TakePictureTask(mTakePictureTaskCallback).execute();  // take picture & upload
    } else if (keyCode == KeyReceiver.KEYCODE_MEDIA_RECORD) {
        changeDestination();  // change channel
    }
}

I tried to add a voice reading function so that I could tell which channel the current posting destination was when I switched Slack channels. But I could not make THETA V’s built-in Text-to-Speech engine speak Japanese. So I gave up.

You can read more about how to make THETA speak in this article: Making the THETA talk (article is in Japanese)

When the shutter button is pressed, the photo can be uploaded like here. (Using a fake image here.)

example slackuploader image

Conclusion

This is still at a rough stage, but images can be posted to Slack directly from your THETA!

Whether using Slack privately or as a team at work, photos taken with THETA can be shared as an easy substitute for a memo.

Right now, the Slackbot token and destination are hard-coded, so it would be nice to be able to set it from a WebUI.

The article below shows details about the WebUI.

Creating a WebUI for Your THETA Plug-in