4
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

超初心者が作ったSlackで動作する日英自動翻訳bot

Last updated at Posted at 2020-03-17

目的

まとめ

  • 英語のみの人、日本語のみの人がストレスなく共存できるSlack環境が欲しい
  • 自動翻訳botを無料で作りたい
  • 必要なときに動作して、うるさくない形で表示できるといい
    • 必要なときに動作→メッセージに対する絵文字リアクションで実装
    • うるさくない形で表示→メッセージに対する返信で実装
  • Google App ScriptとSlack Appを組み合わせて作る

あらまし

Slack超便利だからこれからも使いたい、けど4月から英語のみの外国人研究者が何人かラボに来る、どうしよう・・というのがきっかけ。いろいろやればSlackとGoogle翻訳を自動連携できそうだ、ということはわかった。最初はbotアプリとか自動連携のシステム化を助けてくれるウェブサービスを使ってみたけど、無料期間が終わったら使えなくなってしまった。ウェブアプリやウェブサービスを研究費で買うのは結構面倒ぽい。ああもう自分でやろう・・ということでやってみた。Google App Script(GAS)とSlack Appを組み合わせて実装。

いろいろ調べたけど、結局ここの手順をちょっとだけ変えただけ。こういうの公開してくれるのありがたすぎる・・ということで、自分も公開しようと思い立った。この先似たようなことをやりたくなったときのメモも兼ねる。

僕の知識レベル

  • Slack使用歴は3年くらい
  • プログラムはいろいろ書くけど、研究に使う程度で、大規模なシステム開発とかはしない
  • GASは存在さえ知らなかった
  • 開発期間は、勉強含め1日

全体の構成

t5cxcr8t.jpg

動作

(日英翻訳の例)

  1. 翻訳したいメッセージに対し、ENという絵文字を付けると、Slack Appが起動される
  2. Slack Appはトークンを発行し、公開されたGASを起動、絵文字と文字列を渡す
  3. GASはENという絵文字に応じて文字列を英語に翻訳、Slack botとしてもとのメッセージに返信する形で翻訳文字列を返す

開発手順

  1. カスタム絵文字をSlackに導入
  2. GASの作成
  3. Slack Appの作成
  4. Slack Appの発行したトークンをGASに埋め込む
  5. テスト

1. カスタム絵文字の導入

Q:わざわざカスタム絵文字使わなくてもよくない?実際もとのページはアメリカ国旗と日本国旗使ってるよ?
A:英語使うのはアメリカ人だけじゃないし・・今度来るのは実際カナダ人とオーストラリア人とフランス人だし・・

そんなわけで、日英翻訳用の絵文字には「EN」を、英日翻訳用の絵文字には「JP」を使うことにした。あとついでに、botのアイコン用の絵文字も導入。(ダウンロード EN JP アイコン

以下の手順でカスタム絵文字を追加。「EN」は:english:、「JP」は:japanese:、botアイコン用の絵文字は:gtranslate:にしてみた。
Ej8a9dlR.jpg

とりあえず、絵文字は押せるようになった。当然これだけでは何も起こらない。
x_HzAke-.jpg

2. GASの作成

Google Driveから辿ってGASを立ち上げる。(いつの間にか機能めっちゃ増えてるやん・・)
JyqWC2t8.jpg

GASの初期画面が立ち上がる。うお、Java Script・・(←悪夢が蘇る人)と思いきや、.gs = Google Script。ほぼ一緒みたいだけど?(←何も知らない人)
96uM79Wr.jpg

ここに以下のコードをコピペ。var TOKENのところだけ、Slack Appができてから更新する必要がある。ここではひとまず置いておく。

slack_translate.gb

var TOKEN = "xoxb-YOUR_TOKEN_HERE"; // Slack Appで発行されたトークンをここに埋め込む
var icon_emoji = ":gtranslate:"; // 翻訳botのアイコンを指定。ここを変えれば別のアイコンにもできる

// Slack Appから入力を受け取ってちゃんと正しい返事ができるかのチェック
// 関数名はdoPostである必要がある
function doPost(e) {
  try {
    var json = JSON.parse(e.postData.getDataAsString());
    if (json.type == "url_verification") {
      return ContentService.createTextOutput(json.challenge);
    }
    
    // Slack Appから「Slackで絵文字が追加されたよ」というイベントが来たら、処理を開始
    // https://api.slack.com/events/reaction_added
    // scope: "reactions:read"
    if (json.type == "event_callback" && json.event.type == "reaction_added") {
      // onReactionAddedという関数(下記参照)に飛ぶ
      return ContentService.createTextOutput(onReactionAdded(json.event));
    }
  } catch (ex) {

  }
}

// デバッグ用関数
function postDebugMessage(json) {
  channel = "#__debug";
  text = JSON.stringify(json);
  UrlFetchApp.fetch("https://slack.com/api/chat.postMessage?token=" + TOKEN + "&channel=" + encodeURIComponent(channel) + "&text=" + encodeURIComponent(text));
}

// https://api.slack.com/methods/chat.postMessage
// scope: "chat:write:user" or "chat:write:bot"

// Slackにメッセージをポストする関数
function postThreadMessage(channel, ts, text) {
  if (channel && ts && text) {
    var payload = {
      "channel" : channel, // チャネルを指定
      "text" : text, // messageの中身を指定
      "thread_ts" : ts, //タイムスタンプを指定
      "icon_emoji" : icon_emoji, // botのアイコンを指定
    };
    var option = {
      "method" : "POST", //POST送信
      "payload" : payload //POSTデータ
    };
    
    // トークンに紐付いたアイテム(この場合翻訳前のmessage)に対する返信という形でbotがmessageをSlackに投稿
    UrlFetchApp.fetch("https://slack.com/api/chat.postMessage?token=" + TOKEN, option);
  }
}

// https://api.slack.com/methods/conversations.replies
// "channels:history" or "groups:history"

// Slackからmessageのデータを取得する関数
function getMessages(channel, ts) {
  var response = UrlFetchApp.fetch("https://slack.com/api/conversations.replies?token=" + TOKEN + "&channel=" + channel + "&ts=" + ts);
  var json = JSON.parse(response.getContentText());
  return json.messages;
}

// すでに翻訳済みかチェックする関数
function isTranslated(messages, lang) {
  for (var i in messages) {
    var message = messages[i];
    if (message.text.substring(0, lang.length) == lang) {
      return true;
    }
  }
  return false;
}

function onReactionAdded(json) {
//  postDebugMessage(json);
  var channel = json.item.channel; // イベントの起こったチャネルの名前
  var type = json.item.type; // イベントの起こったアイテムの種類(ここではmessageであることが前提)
  var ts = json.item.ts; // アイテムのタイムスタンプ
  var reaction = json.reaction; // 絵文字の種類
  
  if (type == "message") {
    // Slackからmessageのデータを取得する関数(上記参照)
    var messages = getMessages(channel, ts);
//    postDebugMessage(messages);
    
    if (messages) {
      var message = messages[0].text;
      var languagePrefix;
      var translateTo;
      
      // ENが押された場合
      // すでに翻訳済みかもチェック
      if ((reaction == "english") && !isTranslated(messages, ":english:")) {
        languagePrefix = ":english:";
        translateTo = "en";
      }
      
      //JPが押された場合
      // すでに翻訳済みかもチェック
      if ((reaction == "japanese") && !isTranslated(messages, ":japanese:")) {
        languagePrefix = ":japanese:";
        translateTo = "ja";
      }
      
      if (translateTo) {
        // 翻訳
        var translatedMessage = languagePrefix + " " + LanguageApp.translate(message, "", translateTo);
        // Slackにmessageをポストする関数(上記参照)
        postThreadMessage(channel, ts, translatedMessage);
      }
    }
  }
  return "OK";
}

それらしい名前をつけて保存。

patlSDmQ.jpg

GASは作っただけでは外部から叩くことができない。外から参照できるように、Publishする必要がある。

fVJ4m6iC.jpg

最初はセキュリティの確認が出る。以下の手順で許可を出す。

sqrrS7tV.jpg

これでGASを外から参照できるようになった。web app URLは、あとでSlack Appで参照するので、取っておく。Slack Appを作った後、トークンを取得して、再度GASを編集→Publishの手順がある。

BB_4RXMe.jpg

3. Slack Appの作成

以下の手順で新しいSlack Appを作成。名前をslack_translateとかにしておく。これがBotのユーザ名になる。名前はあとで変えられる。

fLAQVWpz.jpg

作ったSlack Appの設定を行う。まず、Event Subscriptionsにて、絵文字が新たに使用→先ほど公開したGASを起動、という設定。URLの部分は、先ほど取得したGASのURLを入れる。

8RMqNucx.jpg

次に、OAuth & Permissionsにて、GASに与える読み書き権限を決める。この読み書き権限はトークンと紐付いているので、GAS側で読み書きのコマンドを実行しても、トークンによる権限が与えられていないと、実際は何もできない。ここでは、以下の権限を与える。そしてこのSlack Appをひとまずインストール。

  • Bot Token Scopes
    • chat:write ボットがslackにメッセージを送る権限
    • chat:write:customize その際にユーザ名とアイコンを指定する権限
  • User Token Scopes
    • channels:history 公開チャネルのメッセージや関連情報を参照する権限
    • reactions:read 絵文字リアクションや関連情報を参照する権限

meh4scoy.jpg

4. トークンをGASに埋め込む

Slack AppのトークンをGASのTOKEN変数に入れて、再度Publishする。Project VersionはNewを指定する必要がある。Slack AppのEvent Subscriptionsにて再Publish後のURLを貼り付け、Save Changes(この手順必要ないかも?)。そしてOAuth…に移動し再度インストール。

N0vj_ZOm.jpg

5. テスト

正常に動作するかのテスト。
R1schOQF.jpg

ちなみに、Google Translationの上限は、普通に使っていたらよっぽどのことがない限り超えないはず。
iHE15Kdh.jpg

参考にしたページ

4
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?