slackcat - ncコマンド感覚で手軽にSlackに投稿してみる

# はじめに
ｅｃｈｏコマンド感覚で手軽にＳｌａｃｋに通知を送りたいな～と思いつつ、インストールやセットアップが簡単で、コマンドラインオプションもシンプルなツールを探していたのですが、今頃になって見つけました。知ってる人はずいぶん前から知られてるツールのようでした。

この手の欲求に対して、Slack API 自体がシンプルなこともあり、ごく単純にシェルスクリプト組めばいいやんとか、ユースケースが固定ならワンライナーで十分、という声があり、なかなか解決に至りませんでした。
軽くググってもＳＤＫの話しか見当たらず、何かしらスクリプトを書かないといけないのかなぁ、とやる気が起きなかったのですが、なんとか解決策を発見。試した限りではｅｃｈｏコマンド感覚ではなく、ｎｃコマンド（[ｎｅｔｃａｔ](https://nc110.sourceforge.io)）感覚ではあったのですが、手間的には誤差と言うことで。

なお今回紹介するのはＧｏ言語版のツールで、同名の他の言語（RubyGems、Node.js、Python）で書かれたものについては言及しません。それらとは全くの別物となります。

# ＯＳ別インストール方法

## ＦｒｅｅＢＳＤ :new: 
```shell
pkg install slackcat
```
※[簡単](https://cgit.freebsd.org/ports/commit/?id=7ec953eb84b41b8acefcd303877f875fd56f62d6)[インストール](https://www.freshports.org/net/slackcat)できるようにしておきました。

## ｍａｃＯＳ
```shell
brew install slackcat
```
※自分は持ってないので未確認

## Ｌｉｎｕｘ
```shell
sudo curl -Lo /usr/local/bin/slackcat https://github.com/bcicen/slackcat/releases/download/1.7.3/slackcat-1.7.3-linux-amd64
sudo chmod 0755 /usr/local/bin/slackcat
sudo restorecon -F /usr/local/bin/slackcat
```
※x86_64 版以外にも ARM 版あり（ARM 版については性質も含めて未検証）

これも簡単インストールできるディストリビューションがあってもおかしくないよなぁ…と思いつつ。

# 初期設定
Ｓｌａｃｋへの投稿のためには、Slack API を使うための準備が必要になります。ｓｌａｃｋｃａｔではこの設定の単位を「チーム」と呼んでいます。事実上ワークスペースと同義ですが、個人の権限単位でＡＰＩキー（トークン）を発行することから、厳密には「ワークスペース・アカウント」単位で「チーム」と呼称しています。

## Slack App の登録と削除
ｓｌａｃｋｃａｔの Slack App の登録についてですが、作者よりテンプレート[^note_issued]が用意されているため、[Slackcat Auth](http://slackcat.chat/configure/)  [^note_http_url] のページにアクセスすることにより、「チーム」の設定に必要なトークン[^note_token]を取得することができます。

[^note_issued]: ｓｌａｃｋｃａｔ用クライアントＩＤは発行済みです。テンプレートには必要となるスコープ（権限）が設定済みとなっています。
[^note_http_url]: ＨＴＴＰＳではなくＨＴＴＰでアクセスする必要があります。
[^note_token]: ワークスペースとアカウントに紐付けされたアプリ用のトークン＝チーム。

また登録されたｓｌａｃｋｃａｔアプリを削除する場合は、当該ワークスペース・当該ユーザーにてログインした後、「設定とその他管理項目」→「アプリを管理する」→「インストールされたアプリ」から登録された Slack Apps 一覧が表示されます。ここから `slackcat` を探して削除します。
詳細は [ワークスペースからアプリとカスタムインテグレーションを削除する](https://slack.com/intl/ja-jp/help/articles/360003125231)をご覧ください。

## ｓｌａｃｋｃａｔの設定
ごく単純にはトークンだけを書いたファイルを用意すればよいです。
```toml: $HOME/.slackcat
トークン
```
あるいは、複数のトークン（チーム）を使い分けたい場合は、下記のように記述します。
```toml: $HOME/.slackcat
default_team = "チーム名１"
default_channel = ""

[teams]
  チーム名１ = "トークン１"
  チーム名２ = "トークン２"
    :
```

環境変数 `HOME` の設定は必須です。設定が無いと動きません。ＣＲＯＮ等のバッチ処理内で使用する際には、この点に留意してください。また `XDG_` で始まる環境変数がある環境では、`$HOME/.config/slackcat/config` または `$XDG_CONFIG_HOME/slackcat/config` というファイルが必要になります。
本家ドキュメントには書いてないので詳細を調べたところ、下記のようなルールになっていました。

- 環境変数 `HOME`が
    - 無い場合 → エラーで停止
    - ある場合 → `XDG_` で始まる環境変数が
        - 無い場合 → `$HOME/.slackcat`
        - ある場合 → 環境変数 `XDG_CONFIG_HOME` が
            - 無い場合 → `$HOME/.config/slackcat/config`
            - ある場合 → `$XDG_CONFIG_HOME/slackcat/config`
- 上記設定ファイルが
    - 存在しない → エラーで停止
    - 存在する → 設定ファイルが
        - ０行である → エラーで停止
        - １行である → `default` チームのトークンとして解釈
        - 複数行である → 設定に基づいて解釈

# 使い方

## `--channel` オプション

通常 `--channel` （または `-c`）オプションの指定は必須です。しかしデフォルトチャンネルが設定済みの場合、指定しなくても動きます。
以後の説明では、指定されている（表現上省略）、ないしはデフォルトチャンネル設定を参照する、という前提で説明します。

また、このオプションではチームの指定が可能です。その場合は `チーム名:チャンネル名` として指定します。デフォルトチャンネルを指定したい場合、`チーム名:` と指定します。

なおチャンネル名に関しては `#チャンネル名` でも `チャンネル名` でもどちらも行けるようです。ＤＭしたい場合も同じく `@誰か` または `誰か` という指定になります。
詳しくは `slackcat --list` コマンドを実行して確認してみてください。

## オプション指定無し

```console
$ echo 'Hello, World!' | slackcat
```

標準入力の内容をスニペットとしてとりまとめて、メッセージとして送ります。

![slackcat](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/321527/c349a0c8-b0b0-af84-4377-de7dbc1948d7.png)

## `--filename` オプション

```console
$ echo 'Hello, World!' | slackcat --filename sample.txt
```

更にスニペットのファイル名が指定できます。また `--filename` の代わりに `-n` に置き換えることができます。

![slackcat --filename sample.txt](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/321527/3685be83-3ecc-f2e9-a44b-e012e77a4758.png)

## `--filename` および `--comment` オプション

```console
$ echo 'Hello, World!' | slackcat -n sample.txt --comment "This is a pen."
```

更にスニペットに合わせてメッセージを付与できます。
`--comment` だけのオプションで十分なユースケースもあるのですが、残念ながら機能しません。必ず標準入力から読み取ろうとします。

![slackcat -n sample.txt --comment "This is a pen."](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/321527/5ad5f6a2-032c-b2ad-007f-49a7522a9931.png)

## `--stream` オプション

```console
$ echo 'Hello, World!' | slackcat --stream
```

標準入力の内容をスニペットにせず、そのままメッセージとして送ります。また `--stream` の代わりに `-s` オプションと置き換えることができます。

![slackcat --stream](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/321527/fdbd12a3-dd84-6f8f-4e21-c9b0558dea84.png)

## `--stream`、`--thread` および `--comment` オプション

```console
$ echo 'Hello, World!' | slackcat -s --thread --comment "This is a pen."
```

`--comment` で指定した内容がメッセージを、標準入力の内容をスレッドに返信する形で送ります。

![slackcat -s --thread --comment "This is a pen."](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/321527/6a11f1e2-1aed-36c8-cb8c-ce0c9c30643c.png)

## `--thread` および `--comment` オプション

```console
$ echo 'Hello, World!' | slackcat --thread --comment "This is a pen."
```

このケースでは期待した振る舞いをしません。`--thread` オプション無しと同じ振る舞いをします。

## ファイル名の指定

```console
$ echo 'Hello, World!' > /tmp/sample.txt
$ slackcat /tmp/sample.txt
```

`/tmp/sample.txt` をスニペットとして送信します。ファイルは一つしか指定できません。また `--stream` オプションは機能しません。強制的に無効になります。
`--filename`、`--comment` といったオプションは有効です。特に `--filename` オプションを指定しない場合、指定されたパス名からファイル名が決定されます。

# オプション一覧

| オプション名                | 機能                                                                         | 備考                                                       |
|:-|:-|:-|
| `--list`                    | チャンネルリスト一覧の表示                                                   |                                                            |
| `--token 値`                | `default` チームのトークンを指定する。                                       | 設定ファイルを無視します（読みません）。                   |
| `--tee` ／ -t               | ポスト前に標準出力に表示される。                                             |                                                            |
| `--stream` ／ `-s`          | スニペットのアップロードの代わりにメッセージを継続的にストリーミングする。※ | 一連の出力は１メッセージにまとめられます。                 |
| `--channel 値` ／ `-c 値`   | チャンネル名を指定する。                                                     | 具体的な指定については後述。                               |
| `--thread`                  | メッセージに対するスレッド化された返信として後続のメッセージを送信する。※   | メッセージは --comment オプションで指定します。            |
| `--noop`                    | ファイルまたはスニペットのアップロードを行わない。                           | テスト目的とのことだが…。                                 |
| `--username 値` ／ `-u 値`  | 投稿するユーザー名を指定する。                                               | ユーザーを偽装できる権限が必要。                           |
| `--iconemoji 値` ／ `-i 値` | 投稿者アイコンを指定する。                                                   | ユーザーを偽装できる権限が必要。                           |
| `--filename 値` ／ `-n 値`  | ファイルまたはスニペットのファイル名を指定する。                             | 指定が無い場合はファイルパス引数よ決定されます。           |
| `--filetype 値`             | ファイルまたはスニペットのタイプを指定する。                                 | 指定が無い場合はファイル名より決定されます。               |
| `--comment 値`              | メッセージ文を指定します。                                                   | ファイルまたはスニペットが指定された場合に有効になります。 |

- ※ファイルを添付する場合は本機能は無効になります。
- 上記オプション外の引数として、ファイルを指定することが可能です。ただし１ファイルだけ添付となります。
- `--iconemoji` で指定可能な文字（列）「:smile:」の場合 `:smile:` と指定します。
- `--filetype` で指定可能な文字列は [file type](https://api.slack.com/types/file#types) を参照ください。

# よくある質問とその答え

## Ｑ．`slackcat --configure` とは？
Ａ．[初期設定](#初期設定)の一連のシーケンスを誘導するための、インタラクティブな機能となります。
敢えて注目するほどの機能ではないので、見なかったことにしましょう。
複数回実行すると、前の設定を `.orig` サフィックスを付けてリネームするので、追記には向いてないです。

## Ｑ．`slackcat --help` とは？ ここの説明とは若干違うようだが？
Ａ．見なかったことにしましょう。サブコマンド形式で何か出来そうに見えますが、ただのテンプレートそのままで未修正なだけです。オプションだけが有効です。
そのオプションの説明も（長さの問題で難しいところですが）中途半端です。

## Ｑ．`--noop` オプションとは？ テストに向いてるというけど…。
Ａ．ファイルの送信（スニペットおよびファイル指定による）をスキップするだけの機能です。大きなファイルを転送したいケースでのテストには有効ですが、メッセージは送信されるので、完全なテスト（dry run）ではないです。

## Ｑ．本事例では英語だけど日本語は使えないの？
Ａ．そんなことはありません。ＵＴＦ－８であれば日本語で表示できます。

## Ｑ．色々書いてるけど本家ドキュメントを参照すればいいのでは？
Ａ．書いてないので Slack API 読むしかないです。 orz

## Ｑ．複数ファイルを添付したいな。
Ａ．現在の仕様ではできないです（そんな機能は無い）。

## Ｑ．特定のスレッドに投稿したいな。
Ａ．現在の仕様ではできないです（そんな機能は無い）。

# 参考文献
- [Slackcat のホームページ](http://slackcat.chat/) ※ＨＴＴＰＳではアクセスできません
- [bcicen/slackcat: CLI utility to post files and command output to slack](https://github.com/bcicen/slackcat)
- [TOML](https://toml.io/ja/latest)
- [ワークスペースからアプリとカスタムインテグレーションを削除する](https://slack.com/intl/ja-jp/help/articles/360003125231)
- [slack api](https://api.slack.com/lang/ja-jp)
    - [chat.postMessage](https://api.slack.com/methods/chat.postMessage)メソッド
    - [files.upload](https://api.slack.com/methods/files.upload)メソッド
    - [file type](https://api.slack.com/types/file#types)
- [slackcat を使ってみる](https://qiita.com/tearoom6/items/dfebed94f1efbddd8962)
- [slackcatのインストールと使い方](https://qiita.com/keitatata/items/91e0f333061f88285b22)
- [ラズパイにslackcatを導入する](https://qiita.com/winuim/items/6c216087c898de8b27f6)
- [分報をSlackにCLIで投稿する](https://qiita.com/artifactsauce/items/cf149915f6fdacc5e893)
- [CircleCIからSlackにファイルをアップロードする](https://qiita.com/godan09/items/3eeb28dc592a1616d02e)
- [時間のかかるコマンドの実行完了時にslack通知するようにする](https://sunday-morning.app/posts/2020-12-13-slack-notification-when-command-finished)
- [パイプで繋いでコマンドの標準出力の内容をSlackに通知する『slackcat』](https://orebibou.com/ja/home/201610/20161006_001/)