公開日
Slack に安全にテキストを流す Tips
Claude の返信やコマンドの中身を Slack に投稿するときに踏んだ落とし穴と、その対処を短くまとめた。分割送信でサロゲートペアを切らない、リンクの展開を止める、mrkdwn をメンションの形に限る、ログのトークンを伏せるなど。
はじめに
claude-slack-channel は、Claude の返信や、実行しようとしているコマンドの中身を Slack に投稿する。どちらも自分で書いた文ではないので、中身を信用せずに流すしかない。
この記事は、そのために入れた小さな工夫を Tips として並べる。1 つ 1 つは短い話だが、レビューで AI に指摘されて初めて気付いたものも多いんだよな。指摘はちゃんとコードとテストで確かめてから取り込んでいる。
分割送信でサロゲートペアを切らない
Slack のメッセージには長さの上限があるので、長い返信は複数のメッセージに分けて送る。文字数で機械的に切ると、絵文字などのサロゲートペアの真ん中で切れて、文字が壊れる。
分割の処理は、切る位置が上位サロゲートの直後なら 1 文字戻す。コードブロックの途中で切れるときは、フェンスを閉じてから次のメッセージで開き直す。切ったあとに再び上限を超えないか確かめる安全弁も入れた。
この処理は最初から動いていたが、1 つの関数に詰まっていて読みにくすぎた。関数を 5 つに分けるとき、分割前の実装をテスト用に残し、固定シードで 2,400 ケースの入力に対して出力が一致するのを確かめた。挙動を変えない変更は、こうして変えていないことを示す。
途中で失敗したら、何件送れたかを返す
分割して送る途中で失敗すると、Claude には何件目まで送れたかが分からない。分からないと、Claude が全文を再送して重複する。
1 件以上送れていた場合は、エラーに sent=N を含めて返すようにした。1 件目で失敗したときは、元の例外をそのまま投げる。
See more は全文をコードブロックで分けて送る
実行許可の確認で長い入力を省略したとき、See more ボタンで全文を送る。最初は全文を 1 つのブロックで送っていたので、長いと Slack の上限を超えて送れなかった。
全文をコードブロックで包み、上の分割処理で複数のメッセージに分けて送る形に変えた。中身の行頭に ``` があるとフェンスと誤認されるので、ゼロ幅スペースを前に置いて潰す。送り先も、ボタンが押されたスレッドに揃えた。
リンクの展開を無効にする
Claude の返信や、確認の中身に URL が含まれることがある。Slack がプレビューを展開すると、外部へのアクセスが発生して、中身が意図せず表示される。
すべての chat.postMessage に unfurl_links: false と unfurl_media: false を付けた。テストでは、テキスト・markdown・ブロックの各経路で指定が付くことを確かめている。
mrkdwn はメンションの形に限る
許可・拒否のあと、確認のメッセージを「誰が答えたか」付きの表示に書き換える。回答者をメンションにしたいが、mrkdwn として解釈させる範囲を広げると、Claude 側から来た文字列が書式として効いてしまう。
回答者は、Slack のユーザー ID の形のときだけメンションにし、それ以外は plain_text で出す。確認の ID も plain_text で出す。mrkdwn に渡すのは、形を検証した文字列だけ。
同じ理由で、@channel / @here / @everyone とユーザーグループへのメンションは、送信前に無効にする。
ログのトークンを伏せる
ログには Slack のトークンが混ざる経路がある。最初は xoxb- など 6 種類の接頭辞だけを伏せていたが、それだとリフレッシュトークンの xoxe- のようなものが漏れる。
正規表現を xox[a-z]- に広げた。xox の後ろの 1 文字は種別で、将来増えても英小文字なら拾える。xapp- と Bearer も対象にしている。修正の前に、xoxe- が伏せられないことを示すテストを書き、修正後に通した。
空の text を出さない
ツール名・説明・入力内容のどれかが空の確認が来たとき、Block Kit のブロックに空の text を出していた。空の項目は省き、ブロックの組み立てで空の要素を作らないようにした。
小さな話に見えるが、確認の投稿がどこにも成功しなければ、ブリッジはその場で deny を返す。表示の組み立てが、実行の可否に直結する経路だった。
次回は、Windows のコンソールを外から操作するために書いた PowerShell スクリプトの話。