Slack のグループメンションをゲストアカウントでも使えるようにしよう

Slack のグループメンションをゲストアカウントでも使えるようにしよう

こんにちは。
第一事業部に所属しているエンジニアの珍田です。

今回は、一部プロジェクトで導入している Slack ボットを紹介します。

プロジェクトに所属するメンバーが 3 桁の人数規模にもなってくるとさまざまな方が開発に参加されることとなります。
Slack のマルチチャネルゲストで参加されている方も多くなり、運用しているユーザグループも某プロジェクトでは 30 個弱ありました。
Slack では、ゲストアカウントの方はグループメンションを利用できないのですが、これを bot を使うことで(擬似的に)使えるようにしましょう、というのが今回の趣旨です。

少々蛇足ではありますが、なぜこれが必要なのかがピンと来ないかもしれないので、Slack の基本的な機能の説明を先にします。
この問題を把握している方は、最後にある解決編の節に飛んでください。

Slack のユーザグループ

Slack にはユーザグループというしくみがあります。

グループにまとめてメンションができる

ユーザグループを設定すると、そのグループに所属するメンバー全員へメンションをすることができるようになります。
※ ユーザグループの作成は、ワークスペースの設定によるので管理者のみに限定されているかもしれません。

グループに所属すると、関連するチャネルに一括して自動ジョインできる

さらに管理上で便利なのが、デフォルトチャネル(複数のチャネルを設定できます)が設定できることです。

ユーザグループに新たにユーザを追加すると、自動的に設定されているすべてのチャネルにジョインさせることができます。
いちいち、全部のチャネルを回って追加していくという手間がありません。
これはプライベートのチャネルであれば、より威力を発揮します。
パブリックチャネルであれば、本人が直接ジョインできますが、プライベートチャネルではそうもいかないからです。

ユーザグループとゲストアカウント

さて、そんな便利なユーザグループの機能なのですが、残念なことにゲストアカウントでは使用することができません。

ゲストアカウントの種類

ゲストアカウントには、マルチチャネルゲスト、シングルチャネルゲストの 2 種類があります。

1つのチャネルに参加すれば良いのならシングルチャネルゲストで良く、この場合は無料で参加させることができます。
ただし、アカウント数に上限があります。

複数のチャネルに参加させたい場合は、マルチチャネルゲストにする必要があり、こちらは通常のメンバーと同様の費用がかかります。

ゲストアカウントからはチャネル名やユーザグループの名前、ユーザグループのメンバーリストは見えない

では、マルチチャネルゲストは何のためにあるのかというと、公開する情報を制限するためです。

Slack ではパブリックなチャネルの内容はすべて閲覧できますし、チャネルに参加していなくても検索結果に表示されるので、ゲストアカウントにすることで好ましくない情報へのアクセスを制限できます。

また、チャネルの名前自体が知られてはまずい情報である可能性もあります。
ユーザグループに関しても同様です。
ゲストアカウントへは、どういう名前のチャネルやグループがあり、そこに誰が所属しているのかという情報は公開されません。

なお、Slack コネクトを利用すると、Slack を利用している会社同士で連携して、共通のチャネルを使うことができるようになります。
この場合も、相手側のチャネルの情報は見えず、同じ状況となります。
※ 絵文字は連携先のワークスペースのものも見えるので、企業文化が垣間見えておもしろいなと思っています。

ユーザグループとゲストアカウントにおける実用上の問題

その1: グループ名が見えない

上で述べたように、ゲストアカウントからはチャネル名やユーザグループを見ることができません。

そこで、Slack はゲストアカウントには自身がジョインしていないチャネルを #不明なチャンネル、ユーザグループは @不明なグループ といった表示に差し替え、どういう文言だったかがわからないようにしています。

ですが、実用上では、特にユーザグループについては、限定的に特定のユーザグループだけを表示して欲しいといったケースが多くあります。
あるプロジェクトに関係するユーザグループをいくつか作成して運用するケースなどではグループ名がわからないことが障害となります。

たとえば、私が現在関わっているプロジェクトでは、 プロジェクト名+セクション名 といったユーザグループを作成して運用しています。
以下のような感じです(実際にはもっと細かい分割です)。

  • pj-engineer
  • pj-designer
  • pj-qa

この場合、例えば、エンジニア全体にメンションを付けて発言した場合は、

といった感じで発言するわけですが、

ゲストアカウントのユーザからはこの様に見えるので誰に対しての発言なのかがわかりませんし、エンジニアであってもメンションが届くことはありません。

その2: ゲストアカウントはユーザグループのメンションを使用できない

見えないということは、使えないということでもあります。
仮にゲストアカウントのユーザが @pj-engineer としてコメントをしたとしても、Slack はメンションとして認識はしてくれません。

その3: ゲストアカウントはカスタムグループに所属できない

最後の問題点です。

ユーザグループにはゲストアカウントを追加できません。
ですから、例えばエンジニア全員にメンションをしたい場合は、結局 @pj-engineer @dare @sore のようにゲストアカウントのメンバーを列挙する必要があります。
こうすれば、無事エンジニア全員にメンションが届きます。
しかし、PJ の全員が、ゲストアカウントが誰なのかを把握し、毎回正しくメンションをすることは困難です。
ゲストアカウントがユーザグループのメンションを使えない問題は残り、ゲストアカウントはユーザグループを使用せずに全員を列挙する必要があります。

対策案: Slackbot のカスタムレスポンスを導入する

これらの問題の大部分は Slack が用意している Slackbot の機能を使用することで解消することができます。
Slackbot はワークスペースの設定によりますが、Slack の管理ページから誰でも設定できます。

Slackbot を設定する

Slackbot は、別途サーバを用意してボットを動作させる必要がなく、 Slack にあらかじめ用意されているものです。
できることはシンプルで、特定の文字列が入力されたら決まった文言を返してくれます。

ここに、以下のように反応させたいユーザメンションと、返答する文言を設定します。

レスポンスは次のように設定してみます。

きちんと反応して、ユーザグループとゲストアカウントにメンションがされるようになりました。
実際は、ゲストアカウントが使用できるようにするための設定も必要になるかと思います。

ただ、これは試している過程で、以下のようにきちんと全部のアカウントにメンションされない問題があったため、Slackbot を使う運用は諦めました。
マルチバイトのアカウントへのメンションが含まれていたりすると発生するようですが、法則がいまいちわかりませんでした(2年以上前のことで、今どうなっているかは未検証です)。

解決編: Slack bot (not Slackbot) の導入

ようやく本編です。
以上のことを解消するようなボットを導入します。

仕様など

以下の3つの問題を解消しています。

  • ゲストアカウントがユーザグループを認識できない問題
  • ゲストアカウントがユーザグループを利用できない問題
  • ユーザグループにゲストアカウントを含むことができない問題

実装した内容は次の2点です。

  • 特定のユーザグループのメンションがあると、そのコメントにスレッドを作成し、ユーザグループのテキスト( @ を除外した部分)と所属するゲストアカウントへのメンションを投稿する
  • 特定の文字列があると、そのコメントにスレッドを作成し、ユーザグループへのメンションと所属するゲストアカウントへのメンションを投稿する

挙動

実際の挙動を見てみます。

ユーザメンションを投稿した場合

ゲストアカウントには @不明なグループ となりますが、ボットがスレッドに @ 抜きのテキストで投げ返してくれているのでメンションの宛先がわかります
また、同時にグループへ所属しているゲストアカウントへのメンションもしてくれています。

ゲストアカウントがユーザグループへメンションしたい場合

グループメンションは使用できないので、 @ を抜いた文字列を検知すると、スレッドにユーザグループへのメンションを投げ返します。
こちらも、同時にゲストアカウントへのメンションもしています。

この2つの処理で、スレッドになるという煩わしさはあるものの、何も判別できない、メンションがしづらいという問題が解消されたと思います。

なお、Slackbot のカスタムレスポンスのようにそのまま返さずにスレッドへ返信するようにしたのは、どの発言へのレスポンスなのかをはっきりさせたい(めったにないけどレスポンスが遅れるケースもある)のと、現状のプロジェクトの Slack 運用ではスレッドを利用するケースが多いという点からです。

スレッド内でグループメンションをしたい場合

スレッド内で発言した場合でも、その同じスレッド内にボットがレスポンスを返してくれます。

スクリプト

実際に運用しているスクリプトを単純化したものを以下に掲載します。
※ 実際には、もう少しだけ追加機能があったりします。

素の Javascript なので、glitch などを使うと、すぐに動作させることができると思います。
Slack 側の設定はここでは省略します。

const { App } = require("@slack/bolt");
const app = new App({
  signingSecret: process.env.SLACK_SIGNING_SECRET,
  token: process.env.SLACK_BOT_TOKEN,
});

// SLACK のアカウント ID
// ※ @foo の foo ではない。
const groupA = [
  "<@Uxxxxxxxxxx>", // @foo
  "<@Uyyyyyyyyyy>", // @bar
].join(" ");
// ゲストアカウントが所属しないグループは空の配列にしておく
const groupB = [
].join(" ")

// 反応させるメンショングループのリスト
// id: メンショングループの ID
// name: メンショングループの名前
// members: [optional] ゲストアカウントIDの配列
const mentionGroups = [
  { id: "Sxxxxxxxxxx", name: "group-a", members: groupA },
  { id: "Syyyyyyyyyy", name: "group-b", members: groupB },
];

const sendToSlack = async (message, context, text) => {
  try {
    await app.client.chat.postMessage({
      token: context.botToken,
      channel: message.channel,
      thread_ts: message.event_ts,
      text: text,
    });
  } catch (error) {
    console.error(error);
  }
};

mentionGroups.forEach(({ id, name, members }) => {
  // input : group-mention
  // output: @group-mention @member1 @member2 ...
  app.message(new RegExp(`(^(?!@)${name})(\\s|$)`, "m"), ({ message, context }) => {
    sendToSlack(message, context, `<!subteam^${id}|@${name}> ${members || ""}`);
  });

  // input : @group-mention
  // output: group-mention @member1 @member2 ...
  app.message(new RegExp(id), ({ message, context }) => {
    sendToSlack(message, context, `${name} ${members || ""}`);
  });
});

(async () => {
  // Start your app
  await app.start(process.env.PORT || 3000);

  console.log("⚡️ Bolt app is running!");
})();

https://github.com/ckazu/slack-group-mention-bot

ポイントの説明

設定項目

ユーザグループの ID, 名前, (もしいたら)所属するゲストアカウントを設定します。

ユーザグループの ID は、ユーザグループのリストを開いた時の URL を見るのが最も早いです。S から始まる ID です。
ゲストアカウントの ID も、ユーザプロフィールのページの URL を参照すると手っ取り早いです。 U から始まる ID です。

それぞれの ID を取得するには、Slack の API を叩いたり、API のリファレンスサイト上で tester を実行する、といった方法でも良いでしょう。
アカウントの管理は API で自動化したいところです。

API 経由でのメンション

API 経由でユーザへのメンションをするときは、 <@ユーザID> となります(スクリプト内では、横着してユーザリストの配列をこの形で記述しています)。
ユーザグループの場合はちょっと複雑で、 <!subteam^${ユーザグループのID}|@${テキスト}> という形で指定します。

Slack のスレッドへの投稿

thread_ts: message.event_ts,
 のように、受け取ったメッセージの event_ts を取っておいて、 thread_ts としてパラメータに指定します。

おわりに

大規模プロジェクトになってくると、単に開発していくだけでなく、日々の開発業務の効率化やサポートといったことも重要になってきます。
まだまだできていないことも多いのですが、少しずつ安心安全な開発ができる体制を作っていきたいものですね。

株式会社 Aiming では一緒にオンラインゲームを作り上げていく仲間を積極採用中です!
2021年12月1日には、LiTMUS 株式会社(UUUM 子会社)との共同事業契約締結が発表され、やりたいことはますます増えていく状況です!
ゲーム開発にすでに従事されている方も、ゲームクリエイターを目指している方も、業務カイゼンや開発支援といったサポート業務に興味がある方も、ぜひ一度採用ページをご覧になっていただけるとうれしいです!

採用ページはこちらです!