zudo-slack-wisdom
GitHub リポジトリ

検索したい単語を入力

いつでも検索バーを開ける

書式

mrkdwn と Block Kit rich_text、エスケープ、メンション、リンク、日付トークン、そして素の mrkdwn がブロックに勝る場面

mrkdwn: Markdown に似た別物

Slack の mrkdwntext オブジェクト(メッセージの textsection のテキスト、context の要素)の中で使う書式構文だ。*bold*_italic_~strikethrough~`code`、そして 引用の > がある。見た目は Markdown に似ているが Markdown の方言ではない。# による見出し 構文も [text](url) によるリンク構文もなく、太字はアスタリスク 2 つ(**bold**)ではなく 1 つ(*bold*)だ。「Slack 版 Markdown」ではなく、それ自体で完結した小さなフォーマットとして 扱うこと。

&<> のエスケープ

Slack は text オブジェクト内の特別なパースのために &<> を制御文字として使う。 リンクやメンションのトークンを開くわけではない生の < がメッセージ内容に含まれていると、 誤ってパースされてしまう。ユーザー入力など、予測できないテキストを text オブジェクトに 通す前には、この 3 つを必ず HTML エンティティに変換すること。

文字エンティティ
&&amp;
<&lt;
>&gt;

これは mrkdwntext オブジェクトに固有の話だ。API 側でコンテンツが自動的にエスケープ されることはないので、外部由来の文字列(ファイル名、ユーザーのコメント、データベースの値)を メッセージに埋め込む bot は、自分でエスケープしなければならない。

メンション、リンク、チャンネル/ユーザーの参照

これらはすべて同じ <token|optional display text> という角括弧の構文を使う。

対象構文表示
ユーザーメンション<@U012AB3CD>@ メンション。表示名はクライアント側で解決される
チャンネル参照<#C0123456789>#channel-name。クライアント側で解決される
ユーザーグループメンション<!subteam^SAZ94GDB8>そのグループへの @ メンション
@here<!here>チャンネル内のアクティブなメンバーに通知
@channel<!channel>アクティブかどうかを問わず全メンバーに通知
@everyone<!everyone>#general の全メンバーに通知
リンク<https://example.com>自動リンク化された URL
テキスト指定のリンク<https://example.com|Link text>クリック可能なラベルとして Link text
mailto: リンク<mailto:user@example.com|Email User>クリック可能なラベルとして Email User

メッセージ本文中の裸の URL は角括弧なしでも自動リンク化されるが、明示的に囲んだほうが確実で あり、リンクテキストを指定したい時点で必須になる。

日付の書式トークン

<!date^timestamp^token_string^optional_link|fallback_text> は Unix タイムスタンプを クライアント側で、読み手それぞれのロケールとタイムゾーンに合わせてレンダリングする — bot のメッセージで「それは UTC なのか自分の時刻なのか」という曖昧さを避けるための、唯一の 組み込みの手段だ。token_string は次のトークンを 1 つ以上組み合わせて構成する。

トークン出力例
{date_num}2014-02-18
{date}February 18th, 2014
{date_short}Feb 18, 2014
{date_long}Tuesday, February 18th, 2014
{time}6:39 AM(24 時間表記のロケールのクライアントでは 06:39
{time_secs}6:39:45 AM
{ago}3 minutes ago

例: <!date^1392734382^{date} at {time}|February 18th, 2014 at 6:39 AM PST> — パイプの後ろの フォールバックテキストは、トークンに対応していないクライアント(一部の通知面など)で レンダリングされる内容だ。空のままにせず、トークンの出力を妥当に近似した静的な文字列に しておくこと。

mrkdwn と Block Kit の rich_text

mrkdwntext オブジェクトは、書式付きの短い文章 — Section ブロックの本文、Context の 1 行、プレーンテキストの通知 — に適した選択肢だ。Block Kit の rich_text ブロックは、 mrkdwn ではきれいに表現できない構造化コンテンツのために存在する。入れ子の箇条書きや 番号付きリスト、独自の見た目のコンテナを持つコードブロック、そして — リストのセクションに 関係するところでは — Slack リストの text 列への書き込みで唯一受け付けられる形式だ (プレーンな文字列はそのまま拒否される。リストの作成を参照)。 単に書式のついたテキストであれば素の mrkdwn を既定とし、コンテンツに本当の構造がある場合 (リスト、1 つの段落に混在するインラインスタイル)や、書き込み先の面が要求する場合に rich_text へ手を伸ばす。

text オブジェクトを含むレイアウトブロックでは、そのオブジェクトの verbatim フィールドを false にすると、上記の角括弧構文で手書きされていないコンテンツ内のメンションやリンクも 自動的にパースされるようになる。外部から取得したテキストをブロックに転載する場合に有用だ。

実践でのエスケープ

上で見てきた 3 つの変換 — 文字数の予算に合わせて切り詰めること、&/</> をエスケープする こと、そして *bold* のような mrkdwn のマーカーで断片を囲むこと — はこの順番で行う必要が ある。順番を誤ると、それぞれが次の変換の結果を壊してしまう。

  1. まず生のテキストを切り詰める。 エスケープや mrkdwn のラッパーを付ける前に行うこと。 エスケープした後に切り詰めると HTML エンティティを途中で分断してしまう危険がある (&amp; ではなく &am になる、など)し、ラッパーを付けた後に切り詰めるとラッパー自体を 食い荒らしてしまう危険がある。

  2. 次に &<> をエスケープする。 長さが確定した後の生テキストに対して行う。

  3. 最後に mrkdwn のマーカーで囲む。 すでにラッパーが付いた文字列を切り詰めては いけない — *bold* の閉じの * を切り落とすと、その単語が太字でなくなるだけでなく、 切断箇所以降のすべての文字が閉じられていない書式スパンの中に取り残され、その行の残り全体が 崩れてしまう。

URL に対してエスケープ処理をかけてはならない。 エスケープは & を制御文字として扱うが、 多くの URL はクエリ文字列の区切りとして正当に & を含んでいる — リンクトークンの中でそれを &amp; にエスケープしてしまうと、Slack には href の中に & ではなく文字通りの &amp; が 渡ることになり、リンクが壊れるか、自動リンク化が働かなくなる。エスケープ・切り詰め済みの文章と 生の URL は別々の部品として組み立て、両方が完成した後に初めて最終的な <url|text> トークンへ 結合すること — すでに組み上がった文字列をもう一度エスケーパーに通してはならない。

Slack 自身が返すエラーコードも、ログに出す前は信頼できないレスポンスデータとして扱う。 リクエストの一部が外部入力から組み立てられている場合、レスポンスの error フィールドには 攻撃者の影響を受けたコンテンツが混じりうる。中身を確認せずログに埋め込んではならない。厳格な 許可リストパターン — ^[a-z0-9_]+$ はドキュメント化されている Slack のエラーコードを すべてカバーする — に一致する場合のみそのまま出力し、一致しないものは汎用のプレース ホルダーとしてログに残すこと。

Revision History

作成更新