1
/
5

🐲 Slack Next-gen platform で Reaction を぀けるず DeepL でメッセヌゞを翻蚳しおくれるアプリを䜜っおみたした✌ (EDOCODE Advent Calendar 2022🎄)

この蚘事は、| 🎅 EDOCODE Advent Calendar 2022🎄 | 12月16日の蚘事です😀

こんにちは、EDOCODE で採甚を担圓しおいる山田です採甚が䞻な仕事ですが、EDOCODE の組織党般に責任を持っおいたす。

今回は、

  • EDOCODE の瀟内の様子を知っおもらう
  • 最近リリヌスされた Slack Next-gen platform でのアプリの䜜り方を知っおもらう

をネタに、Advent Calendar の蚘事を曞いおいこうず思いたす🙌

぀くるアプリ

タむトルにもあるずおり、「Reaction を぀けるず DeepL でメッセヌゞを翻蚳しおくれるアプリ」を䜜成したした。䟋えば Slack に日本語のメッセヌゞがあり、それを英語に翻蚳しおほしい堎合は、日本語の元メッセヌゞに専甚のリアクションを぀けたす。そうするず、DeepL 翻蚳された英語のメッセヌゞがそのチャンネルに投皿されたす。元メッセヌゞが英語の堎合は、日本語の翻蚳メッセヌゞが投皿されたす。

少し話がそれたすが、EDOCODE では予定の空いおいる瀟員がランダムで 4人遞ばれるランチ䌚が毎日行われおいたす。そのランチを抜遞しおいるアプリに "Puff" ずいう名前が぀いおいるのですが、今回の翻蚳アプリも Puff のキャラクタヌを䜿い、ランチではなく DeepL 翻蚳しおくれる機胜を持っおいるこずから "Deep Puff" ずいう名前にしおいたす。

こちらのアむコンは珟行バヌゞョンのもので、2幎前に開発したした。今回は、Slack から 2022幎9月にβリリヌス された "Slack Next-gen platform" を觊っおみたいなヌず思っおいたので、このアプリをれロから䜜り盎しおみたした。

ちなみに Puff ずいうのは、Peter Paul & Mary の『Puff, The Magic Dragon』ずいう曲が元ネタです🐲🎞

なぜこのアプリがあるず良いのか

背景

EDOCODE には 2022幎11月珟圚で 17人のメンバヌが圚籍しおいたす。そのうち、玄 35% にあたる 6人が日本語ネむティブではない人です。぀たり 3分の1 のメンバヌが日本語ネむティブではありたせん。たたこれたでは採甚芁件で「流暢な日本語」を必須にしおいたのですが、埐々にこの芁件を緩和しおいたす。実際、6人のうち盎近で入瀟した 1人は、ただ日本語でのコミュニケヌションがずれないメンバヌです。

さらに新芏プロダクトの PUSHCODE では、最初から「グロヌバルなチヌムで」「グロヌバルにサヌビスを提䟛する」こずを前提に立ち䞊げを行いたした。䌚瀟党䜓が "玔ゞャパニヌズ" な䌚瀟から、少しず぀ "英語フレンドリヌ" な䌚瀟ぞず転換しおいく過皋の䞭にいたす。こうした背景から、日英語の翻蚳が必芁な堎面が以前から増えおきおいたした。

䌚瀟を英語化するこずの難しさ

EDOCODE はずっず日本語でやっおきた䌚瀟ですが、それを英語化するのはなかなか難しいです。ですが、䌚瀟の英語化は䞖の䞭の趚勢ずしお埅ったなしでもありたす。ここでトップダりンで転換を急ごうずしたずころで、反䜜甚があるのは確実に思えたす。そもそもトップが英語ができない問題がありたすし・・🥲 むしろ皆さんから英語を教えおいただいおいる毎日です🙇🏻

経営メンバヌず盞談しお、そういう方針でいこうず特に決めたわけではないのですが、少しず぀英語に抵抗感をなくすような、気づいたら英語がなんか身近になっおたわ、ずいう感芚を日本語ネむティブの瀟員に持っおもらうのが良いんじゃないかなヌず思い、そういう気持ちず狙いで個人的にはここ 2幎ちょっずの間、EDOCODE の英語化を進めおきたした。

DeepL の登堎

ちょうどコロナ犍が始たった 2020幎の初頭に DeepL が日本語に察応 したした。振り返っおみるず、リモヌトワヌク環境ず DeepL の登堎が EDOCODE の英語化にずっおずおもタむミング良かった気がしたす。リモヌトワヌクにより察面や口頭でのコミュニケヌションが盞察的に枛り、Slack や Notion によるテキストコミュニケヌションが増えたした。そうするず、仮に英語で情報が流れおきおも、DeepL すれば倧半のこずは問題ありたせん。

この蟺りで、「日本語・英語・䞭囜語であれば、自分ず盞手ずの間で䞀番コミュニケヌションしやすい蚀語で Slack しおも良いですよ」ず瀟内にアナりンスした気がしたす。同じチャンネルにいる人で英語が読めない人でも、その人が非同期的に DeepL すれば問題ないですし、䌚瀟ずしおはコミュニケヌションコストが䜎く、より生産的な意思疎通をしおもらったほうが良いからです。ずは蚀え、ただこの時期は衚立っお、ある意味で「遠慮なく」そういうこずをやる瀟員は出おきたせんでした。

ようやく今になっお掻躍の機䌚が・・

珟行バヌゞョンを䜜成した 2幎前は、䞊蚘のような感じでした。耇数人が参加しおいるプロゞェクトごずのチャンネルでは、みんな日本語でポストするのが基本。1察1 の個別の DM では䞀番快適な蚀語で話をしおいたず聞いおいたす。ですが䌚瀟ずしおは「もっず英語ずか䞭囜語ずか、みんなが䞀番自分の考えをネむティブに衚珟できる蚀語で発信しお良いですよ」ずいうスタンスです。普通に考えたら、「理想を蚀うのは簡単だけど、実際そんなこずされたら倧倉でしょ」ず思うのが圓たり前です。

で、長くなりたしたが、そういう気持ちが普通だったらみんな持぀だろうな〜〜ず思い、じゃあリアクションを぀けるだけで翻蚳しおくれる Slack アプリを䜜れば、各自の翻蚳にかかるコストも心理的コストも䞋がるのではず思い、Deep Puff を䜜成したのでした。結果ずしおは、ほが誰も䜿っおくれずに 2幎が過ぎおいたした😅この蚘事を曞くためにさっき commit log を調べたら 2幎前に開発しおいおびっくりしたした

そしお珟圚はずいうず、䞊でも觊れた日本語がただできない新入瀟員のために倧掻躍しおいたす。EDOCODE は劎務たわりの業務をグルヌプ䌚瀟である Wano に委蚗しおいたすが、Wano 偎の劎務担圓の方は英語ができたせん。そこで Deep Puff を䞡名に玹介しお、劎務関係のコミュニケヌションをやっおいただいおいたす。もしこのアプリがなかったら、かなり倧倉だったのではないかず思いたす。自画自賛ですが。

↓こんな感じのやりずりを蚀語の問題なく、たた手間をかけずにできおいたす🙂

Slack Next-gen platform

前半は EDOCODE の様子をご玹介したした埌半は Slack の次䞖代プラットフォヌムでのアプリの䜜り方を簡単にご玹介したいず思いたす💪 たぶんこの手の蚘事は Qiita や Zenn などの技術情報メディアで詳しく解説されおいるず思いたすので、この蚘事ではざっくりずだけ玹介したいず思いたす。

特城

このプラットフォヌムの倧きな特城は以䞋があるず思いたす。圱響床の高い順で。

  • Slack がむンフラを甚意しおくれる
  • CLI が充実しおいる
  • Bolt SDK よりもなんか䜜りやすい
  • Deno で開発できる

䜕よりも良いず感じたのは、Slack が実行環境を甚意しおくれるずころです。これがめちゃくちゃ良いです。珟行の Deep Puff やシャッフルランチをサポヌトしおいる Puff Lunch は Firebase Functions で実行しおいたす。たずこういうのがいりたせん。なので䜜っお動かすたでのハヌドルがかなり䜎いです。たた Slack の倖で実行する堎合、Slack の Trigger を listen するためのコヌドから曞き始める必芁がありたした。もっず蚀えば、Trigger した堎合にどの URL ぞリク゚ストを送るだずか、認蚌がどうだずか、そういう「やるべき蚭定」がたくさんありたした。😮‍💚 こうしたものが党おなくなったずいうのは、かなり画期的な気がしたす。

導入手順

めっちゃ簡単なので、説明の必芁がないぐらいです、ずいう説明をしたいがためのパヌトです。😌 公匏のペヌゞにあるむンストラクションどおりにやっおいけば、䜕も問題はありたせん。以前はもっず倧倉でしたが、新しいプラットフォヌムでは、

  1. CLI をむンストヌルする
  2. CLI で認蚌を通す
  3. コマンドで create slack app する
  4. コマンドで trigger を䜜成する
  5. ロヌカル環境でアプリを起動する

で開発を開始できたす。はやすぎ

各機胜の玹介

たったく初めおの方にずっお、以䞋の機胜を把握しずけばだいたいアプリが䜜れたす、ずいうものを簡単に玹介しおおきたす。開発䞭によく参照した Slack の公匏ドキュメントのペヌゞをベヌスにたずめおいたす。

Triggersトリガヌ

  • アプリの働きを起動する「きっかけ」の郚分
  • 䜕があったらアプリの凊理を始めるのずいうこずを定矩したす
  • Link, Scheduled, Event, Webhook の 4぀のトリガヌがありたす
  • 今回䜜った Deep Puff は「リアクションしたら」なので Event Trigger の reaction added を利甚しおいたす

Workflowsワヌクフロヌ

  • 起動したらどんな順番でどんな凊理をしおいくのかを定矩したす
  • 凊理に察しおの入力を決めたり、凊理が終わった埌のデヌタを出力しお、次に枡したりもできたす
  • Slack ぞ䜕かの動䜜を行わせたいずきも、ここに曞くこずができたす

Functions関数

  • 具䜓的な凊理を蚘述したす
  • 関数には以䞋にある Buit-in Functions ず Custom Functions の぀のタむプがありたす

Buit-in Functions組み蟌み関数

  • Slack が事前に甚意しおくれおいる関数です
  • よく䜿う Slack ず連携した凊理が甚意されおいお䟿利です
  • 䟋えば「Slack にメッセヌゞを送る」などは Built-in Function があるのですぐに実装できたす

Custom Functions独自関数

  • 開発者独自の凊理を定矩できたす
  • 䟋えば Slack 倖のサヌビスずの連携を曞くこずができたす
  • Slack の各皮 API を利甚し぀぀、色んな凊理を組み合わせるこずももちろん可胜です

Slack API MethodsSlack API のメ゜ッド

  • どうやっおこのプラットフォヌムで Slack API を利甚するのずいう説明が曞かれおいたす
  • 玠晎らしいのは、埓来のように Slack API を䜿うための Token などを管理し぀぀コヌドに曞く必芁がないこずです
  • なぜなら、ここ にあるずおり token ずいう匕数を枡すだけで、コンテキストずしおそれを利甚できるからです
    • これは Slack のむンフラで動かしおいるからなせるこずですね😀

Third-party API Calls倖郚 API の呌び出し

  • 倖郚 API を呌ぶにはどうしたら良いのずいう説明が曞かれおいたす
  • 認蚌情報はハヌドコヌディングせずに .env に曞いお、コンテキストを利甚しお䜿っおね、ず瀺されおいたす
  • 公匏のサンプルコヌドは Deno の fetch で玹介されおいたすが、Deep Puff は Ky を利甚したした

Deploy to Slackデプロむ

  • 本番甚にデプロむする際にはアプリのビルドが走りたす
  • このずき、自分で関数を定矩しおいるず TypeScript のビルドチェックが入りたす
    • 自分は恥ずかしながら TypeScript が初めおだったので、゚ラヌを芋ながら型泚釈を぀けおいくのに 2時間ぐらいずられおしたいたした・・😓
  • このペヌゞ自䜓には特に重芁な情報はありたせん

実際のコヌドサンプル

最埌に実際のコヌドサンプルを掲瀺しおおきたす。興味ない人はここで読み終えおください

Triggers

// #/triggers/deep_puff_trigger.ts

import { Trigger } from "deno-slack-api/types.ts";
import DeepPuffWorkflow from "../workflows/deep_puff_workflow.ts";

const deepPuffReactionTrigger: Trigger<typeof DeepPuffWorkflow.definition> = {
  type: "event",
  name: "Deep Puff Reaction Trigger",
  description: "This trigger will run by adding the Deep Puff reaction.",
  workflow: "#/workflows/deep_puff_workflow",
  event: {
    event_type: "slack#/events/reaction_added",
    channel_ids: ["xxxxxxxx"],
    filter: {
      version: 1,
      root: {
        operator: "OR",
        inputs: [{
          statement: "{{data.reaction}} == deep-puff"
        },
        {
          statement: "{{data.reaction}} == deep-puff-right"
        }],
      },
    },
  },
  inputs: {
    user_id: {
      value: "{{data.user_id}}",
    },
    message_ts: {
      value: "{{data.message_ts}}",
    },
    channel_id: {
      value: "{{data.channel_id}}",
    },
  },
};

export default deepPuffReactionTrigger;
  • Trigger は event で、その type を reaction_added ず定矩
  • filter で :deep-puff: たたは :deep-puff-right: ずいうリアクションがあったずきのみトリガヌさせる
  • inputs では reaction_added から枡っおくるデヌタのうちどれどんな名前で䜿うのかを定矩

Workflows

// #/workflows/deep_puff_workflow.ts

import { DefineWorkflow, Schema } from "deno-slack-sdk/mod.ts";
import { RetrieveMessageFunctionDefinition } from "../functions/retrieve_message_function.ts";
import { DeepLTranslatingFunctionDefinition } from "../functions/deepl_translating_function.ts";

const DeepPuffWorkflow = DefineWorkflow({
  callback_id: "deep_puff_workflow",
  title: "Deep Puff workflow",
  description: "Deep Puff Translating by Adding reaction.",
  input_parameters: {
    properties: {
      user_id: {
        type: Schema.slack.types.user_id,
      },
      message_ts: {
        type: Schema.types.string,
      },
      channel_id: {
        type: Schema.slack.types.channel_id,
      },
    },
    required: ["user_id", "message_ts", "channel_id"],
  },
});

const retrieveMessageFunctionStep = DeepPuffWorkflow.addStep(
  RetrieveMessageFunctionDefinition,
  {
    channel_id: DeepPuffWorkflow.inputs.channel_id,
    message_ts: DeepPuffWorkflow.inputs.message_ts,
  },
);

const deepLTranslatingFunctionStep = DeepPuffWorkflow.addStep(
  DeepLTranslatingFunctionDefinition,
  {
    text: retrieveMessageFunctionStep.outputs.message,
  },
);

DeepPuffWorkflow.addStep(Schema.slack.functions.SendMessage, {
  channel_id: DeepPuffWorkflow.inputs.channel_id,
  message: deepLTranslatingFunctionStep.outputs.translated,
});

export default DeepPuffWorkflow;
  • DefineWorkflow では入力デヌタのバリデヌションを定矩しおいる
  • ワヌクフロヌには 3぀のステップを远加しおいる
    • (1) リアクションの event 情報から、該圓するメッセヌゞを取埗するステップretrieveMessageFunctionStep
    • (2) 取埗したメッセヌゞのテキストを DeepL API で翻蚳するステップdeepLTranslatingFunctionStep
    • (3) 翻蚳枈みのテキストをリアクションが぀いたメッセヌゞず同じチャンネルにポストするステップSchema.slack.functions.SendMessage

Functions

// #/functions/retrieve_message_function.ts

import { DefineFunction, Schema, SlackFunction } from "deno-slack-sdk/mod.ts";
import { SlackAPI } from "deno-slack-api/mod.ts";

export const RetrieveMessageFunctionDefinition = DefineFunction({
  callback_id: "retrieve_message_function",
  title: "Retrieve Messge Function",
  description: "Retrieving a message which is added reaction.",
  source_file: "functions/retrieve_message_function.ts",
  input_parameters: {
    properties: {
      channel_id: {
        type: Schema.slack.types.channel_id,
      },
      message_ts: {
        type: Schema.types.string,
      },
    },
    required: ["channel_id", "message_ts"],
  },
  output_parameters: {
    properties: {
      message: {
        type: Schema.types.string,
        description: "Message to be translated.",
      },
    },
    required: ["message"],
  },
});

export default SlackFunction(
  RetrieveMessageFunctionDefinition,
  async ({ inputs, token }) => {
    const client = SlackAPI(token);
    const res = await client.conversations.history({
      channel: inputs.channel_id,
      oldest: inputs.message_ts,
      inclusive: true,
      limit: 1,
    });

    if (res.ok) {
      const message = res.messages[0].text;
      return { outputs: { message } };
    } else {
      throw "Error: on Slack conversations.history()";
    }
  },
);
  • Slack API methods の conversations.history を䜿った凊理
    • import { SlackAPI } しお、コンテキストの { token } を匕数に枡すだけで OK なのでずおも楜
    • conversations.history() ではタむムスタンプで指定したメッセヌゞを取埗しおいるoldest, inclusive, limit
// #/functions/deepl_translating_function.ts

import { DefineFunction, Schema, SlackFunction } from "deno-slack-sdk/mod.ts";
import { SlackAPI } from "deno-slack-api/mod.ts";
import ky from 'https://esm.sh/ky';

export const DeepLTranslatingFunctionDefinition = DefineFunction({
callback_id: "deepl_translationg_function",
title: "DeepL Translating Function",
description: "Translating text via DeepL API.",
source_file: "functions/deepl_translating_function.ts",
input_parameters: {
properties: {
text: {
type: Schema.types.string,
description: "Text to be translated.",
},
},
required: ["text"],
},
output_parameters: {
properties: {
translated: {
type: Schema.types.string,
description: "Message translated.",
},
},
required: ["translated"],
},
});

const translate = async (source: string, authKey: string, targetLang = "JA") => {
const host = 'api.deepl.com';
const v = 'v2';
const type = 'translate';
const url = 'https://' + host + '/' + v + '/' + type;
const headers = {
'Content-Type': 'application/x-www-form-urlencoded',
Authorization: "DeepL-Auth-Key " + authKey,
};
const text = source.replace(/<@\w+>/g, "");
const searchParams = { text, target_lang: targetLang };

interface Translation { detected_source_language: string, text: string }
interface DeepLResponse { translations: Translation[] }
const res: DeepLResponse = await ky.post(url, { headers, searchParams }).json();
let translated: string = res.translations[0].text;

if (res.translations[0].detected_source_language === targetLang) {
translated = await translate(source, authKey, "EN");
}

return translated;
}

export default SlackFunction(
DeepLTranslatingFunctionDefinition,
async ({ inputs, env }) => {
const source = inputs.text;
const authKey = env.DEEPL_AUTH_KEY;

const translated = await translate(source, authKey);

return { outputs: { translated } };
},
);
  • HTTP クラむントには Ky を䜿った
  • DeepL API は 公匏のドキュメント を参照
    • 利甚は有料のアカりントが必芁です💵
  • 独自に定矩しおいる translate() はデプロむ時に TypeScript のチェックが入るため、型泚釈を各所に远加しおいる
  • const text = source.replace(/<@\w+>/g, ""); は DeepL に枡す翻蚳元テキストから Slack のナヌザヌ ID などの英数字を削陀しおいる
  • 翻蚳は、日本語だったら英語、英語だったら日本語に翻蚳させるずいうのがアプリの芁件
    • DeepL は API のレスポンスで入力したテキストの蚀語刀定が返っおくる
    • それを芋お、もし翻蚳元テキストが日本語だった堎合は、翻蚳先蚀語targetLangを英語に指定しお再床リク゚ストを投げ盎しおいる実装ずしおは再垰しおいる
  • DeepL API の認蚌情報は .env に保管しお { env } のコンテキストから呌び出しおいる
    • Firebase Functions の珟行バヌゞョンだず、.env をやるにも NPM パッケヌゞが必芁だったので、本圓にありがたい🙏

たずめ

ずいうこずで、EDOCODE のちょっずした文化ず、それをサポヌトするためのツヌルを䜜った話をご玹介したした😀

人事の担圓者がこういう簡単なプログラミングができるず、組織マタヌで「こういうの欲しい」ずなったずきにパッず぀くっお瀟内に提䟛できるので䟿利だなず感じたす。普通の䌚瀟だず゚ンゞニアは事業をたわす郚眲にいたりするので、こういうものは有志の゚ンゞニアが空き時間に぀くっおくれるのを期埅したり、お願いしたりするこずが倚いのかなず。Slack Next-gen platform により、さらにグッずプログラミングの芁求レベルが䞋がったので、これを機に非゚ンゞニアやバックオフィスの担圓者がどんどん必芁なシヌンでアプリを぀くっおいけるのではないかず思いたす🙌

珟行バヌゞョンでは開発に 2, 3日はかかったず思いたすが、今回のリニュヌアルでは開発環境の孊習も含めお合蚈 12時間ぐらいしかかからなかったず思いたす。ハマりポむントが結構あったものの、そこさえクリアすればめちゃくちゃ簡単に実装できおしたうので、本圓に䟿利だず思いたす今さらですが、ハマったポむントを解説するべきでしたたぶんそういう蚘事があればもっず早く開発できおた。

新しいバヌゞョンのアプリアむコンです👇🐉 Figma で適圓にマッシュアップしお䜜りたした👚‍🍳

お知らせ

EDOCODE では、゚ンゞニア・デザむナヌ・プロダクトマネゞャヌを募集しおいたす🚀

ご興味のある方は、
   ぜひ Wantedly で「話を聞きに行きたい」👇👇👇 しおください

たた EDOCODE の採甚ペヌゞもありたすので、こちらもぜひご芧ください🙌
https://go.edocode.co.jp/jobs

EDOCODE株匏䌚瀟では䞀緒に働く仲間を募集しおいたす
11 いいね
11 いいね
同じタグの蚘事
今週のランキング