[小ネタ]Slackアプリでメンションやチャンネルのリンクがどう扱われるか確認してみた

2020.04.02

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

CX事業本部の阿部です。

今仕事のサポートツールとしてSlackアプリを作っています。 その中でSlackのメッセージに記載されるメンションやチャンネルの情報がアプリ上でどう扱われているか気になったので、いくつかパターンを設定して試してみました。

確認すること

  • ユーザおよびユーザグループのメンションをアプリで受け取る
    • ユーザおよびユーザグループの違いを見て、APIから情報を取得する
  • メッセージ内にチャンネルへのリンクがあるメッセージをアプリで受け取る
  • ユーザ/ユーザグループ/チャンネルへのリンクを埋め込んだメッセージをアプリから返す

前提

今回のサンプルコードは全てBoltフレームワークを使っています。

ユーザおよびユーザグループのメンションをアプリで受け取る

app.message("hello", ({message, say}) => {
    console.log(message);
});

このようにユーザとユーザグループにメンションを送ってみます。

Slackアプリで受け取ったメッセージは以下のようになりました(IDなどはマスクしています)。

{
  client_msg_id: '省略',
  type: 'message',
  text: '<@発信者のユーザID> <@ボットユーザID> <!subteam^S01199KF2TS|@ユーザグループのハンドル> hello',
  user: '発信者(私)のユーザID',
  ts: '1585787757.000600',
  team: '省略',
  blocks: [ { type: 'rich_text', block_id: 'kKd', elements: [Array] } ],
  channel: '省略',
  event_ts: '1585787757.000600',
  channel_type: 'channel'
}

わかること

  • メンションはIDに展開されてSlackアプリに渡ってくる。その形式は <@[9桁の英数字]>
  • ユーザグループも同様にIDに展開されてSlackアプリに渡ってくる。その形式は <!subteam^[11桁の英数字]|@ユーザグループのハンドル>

ユーザおよびユーザグループの違いを見て、APIから情報を取得する

アプリとユーザグループの展開のされ方の違いをみたので、次はそれぞれの情報を取得してみましょう。 なお、以下のコードの実行にはアプリの権限に users:readusergroups:read の許可が必要になります。

function getMentionsUser(text: string): RegExpMatchArray {
    const regIdPattern = /<@[A-Z0-9]{9}>/g;

    return text.match(regIdPattern);
}

function getMentionsGroup(text: string): RegExpMatchArray {
    const regIdPattern = /<!subteam\^[A-Z-0-9]{11}\|/g;

    return text.match(regIdPattern);
}

app.message("hello", async ({ message, say }) => {
    const mentionedUser = getMentionsUser(message.text).map(mention => mention.substr(2,9));

    const userinfo = await app.client.users.info({
        token: 省略,
        user: mentionedUser[0]
    });

    console.log(userinfo);

    const mentionedGroup = getMentionsGroup(message.text).map(group => group.substr(10, 11));

    const groupInfo = await app.client.usergroups.users.list({
        token: 省略,
        usergroup: mentionedGroup[0]
    });

    console.log(groupInfo);
});

先ほどと同じメッセージを送ると、以下のように取得できます。

ユーザ(私へのメンションなので私の情報が取れています):

{
  ok: true,
  user: {
    id: '省略',
    team_id: '省略',
    name: 'abe.shinsuke',
    deleted: false,
    color: 'a63024',
    real_name: '阿部 信介 abe shinsuke',
    // タイムゾーンの項目省略
    profile: {
      title: 'エンジニアチームオーガナイザー',
      phone: '',
      skype: '',
      real_name: '阿部 信介 abe shinsuke',
      real_name_normalized: '阿部 信介 abe shinsuke',
      display_name: '阿部信介',
      display_name_normalized: '阿部信介',
      // いくつか項目省略
    },
    // いくつか項目省略
  },
  response_metadata: {
    scopes: [省略],
    acceptedScopes: [ 'users:read' ]
  }
}

ユーザグループ(私が参加しているユーザグループなので私のIDが含まれています):

{
  ok: true,
  users: [ '私のユーザID' ],
  response_metadata: {
    scopes: [省略],
    acceptedScopes: [ 'usergroups:read' ]
  }
}

メッセージ内にチャンネルへのリンクがあるメッセージをアプリで受け取る

今度は趣向を変えてチャンネルへのリンクを受け取ってみましょう。

このようなメッセージを送ってみます。

Slackアプリで受け取ったメッセージは以下のようになりました。

{
  client_msg_id: '省略',
  type: 'message',
  text: 'hello <#チャンネルID|チャンネル名>',
  user: '省略',
  ts: '1585790806.003000',
  team: '省略',
  blocks: [ { type: 'rich_text', block_id: 'uamp1', elements: [Array] } ],
  channel: '省略',
  event_ts: '1585790806.003000',
  channel_type: 'channel'
}

わかること

  • チャンネルもユーザグループ同様にIDに展開されてSlackアプリに渡ってくる。その形式は <#[9桁の英数字]|チャンネル名>

チャンネルの情報取得も権限を設定して、適切なAPIに投げてみるだけなので省略します。

ユーザ/ユーザグループ/チャンネルへのリンクを埋め込んだメッセージをアプリから返す

最後に逆パターンとして、メッセージに埋め込んでアプリに返してみましょう。

今まで取得した情報をメッセージとして返すようにしてみます。 おそらく取得時に展開していた形式でメッセージを返せば通知が飛んでくるし、チャンネルのリンクも貼られると思います。

app.message("hello", async ({ message, say }) => {
    say("you mentioned <@私のユーザID> and <!subteam^ユーザグループID|@ユーザグループのハンドル> ! look channel <#チャンネルID|チャンネル名>");
});

ちゃんとメンションもきましたし、チャンネルのリンクもたどれます。

と、ここでふと気になりました。

ユーザグループのハンドルとチャンネル名は省略可能か

まあ、これは省略可能なのではないかと予想しています。

この形式でメッセージを返すようにしてみましょう。

app.message("hello", async ({ message, say }) => {
    say("you mentioned <@私のユーザID> and <!subteam^ユーザグループID> ! look channel <#チャンネルID>");
});

やはり予想通りですね。ハンドルやチャンネル名も表記されています。

ユーザグループのハンドルとチャンネル名があってない場合はどうなるのか

最初に使った以下の形式で、ハンドルとチャンネル名をでたらめにしてみましょう。

app.message("hello", async ({ message, say }) => {
    say("you mentioned <@私のユーザID> and <!subteam^ユーザグループID|@ユーザグループのハンドル> ! look channel <#チャンネルID|チャンネル名>");
});

以下のように帰ってきました。メンションも飛んできます。

ということはつまり、アプリ側から返す場合は形式のIDまで見て、ハンドルやチャンネル名が書いてあっても無視される、ということのようです。

まとめ

Slackアプリを作る上で、メンションからユーザに対してアクションを起こしたりチャンネルの情報を利用したりはよくあることなので、どのようにSlackアプリで扱えばいいのか確認できてよかったです。