Slack でタコピー風に会話する

「Slackでの文章が怖い」と言われたので何か改善できないかと思い、まずはメッセージを全てタコピー風にする CLI を Go で作成しました。（GitHub でスターをくれると喜びます。）

https://github.com/Code-Hex/takopi

## タコピー？

タコピーは「タコピーの原罪」という少年ジャンプ＋で連載中の漫画のキャラクターです。「っピ」を語尾につけるのが特徴なので文章を柔らかく見せてくれるだろうと考え採用しました。

https://shonenjumpplus.com/episode/3269754496638370192

## CLI の作成

Go 有名な形態素解析ライブラリ [github.com/ikawaha/kagome](https://github.com/ikawaha/kagome) を利用しています。

文章を加工するためにいくつか工夫しました。

1. メッセージを正規化する
2. 正規化済みの文字列に対して形態素解析を行う
3. 置き換えたい内容が見つかれば置換する

置き換えたいものは map として[定義](https://github.com/Code-Hex/takopi/blob/7a18de474ab2f338d0525f00bdce6d4ed9ee25f1/dict.go#L9)しました。map のキーの型には構造体を利用しています。この構造体のフィールド全てが比較可能な型になるので、構造体も比較可能になります。（comparable）

比較可能にすることでこのように置き換え可能な内容かを簡単に判定することが可能です。

```go
for _, token := range tokens {
	features := token.Features()
	key := key{
		pos:   features[0],
		affix: features[1],
		want:  token.Surface,
	}
	if replace, ok := dict[key]; ok {
		buf.WriteString(replace) // 置き換え
	} else {
		buf.WriteString(token.Surface)
	}
}
```

リポジトリでバージョンタグを切ると GitHub Action 上で [goreleaser](https://goreleaser.com/) がいい感じに[各プラットフォーム向けのバイナリを作成](https://github.com/Code-Hex/takopi/releases)してくれます。とても便利です。また homebrew でインストールできるようにも調整してくれます。

```sh
$ brew update
$ brew install Code-Hex/tap/takopi
$ # argv
$ takopi "はい、いいえ、どうだ"
そうだっピ、ちがうっピ、どうだっピ！
$ # stdin
$ echo "はい、いいえ、どうだ" | takopi
そうだっピ、ちがうっピ、どうだっピ！
```

これで温かみのあるコミュニケーションができますね。

## モバイル上での対応

外出先で Slack のメッセージが届いたので返信しようとしました。その時に気がついたのですが、なんとモバイル上で CLI を動かすことができません。（一旦なりすましました）そこで Wasm にしてブラウザでも実行できるようにしました。

[Go のコード](https://github.com/Code-Hex/takopi/blob/main/cmd/wasm/main.go)はとてもシンプルなものになりました。Go の Wasm は JavaScript のライブラリとして利用するというより、あくまで main プログラムを実行する形になっているようです。

```go
package main

import (
	"syscall/js"

	"github.com/Code-Hex/takopi"
)

func takopilize(_ js.Value, args []js.Value) interface{} {
	if len(args) == 0 {
		return nil
	}
	return takopi.Do(args[0].String())
}

func registerCallbacks() {
	js.Global().Set("takopilize", js.FuncOf(takopilize))
}

func main() {
	c := make(chan struct{}, 0)
	registerCallbacks()
	println("takopi WebAssembly Ready")
	<-c
}
```

このコードを`takopi.wasm` としてビルドします。そして `$GOROOT` から動かすために必要な js ファイルをコピーしてくる必要があるらしいです。とてもクセがありますね。Makefile の中身はこのようになりました。

```make
.PHONY: all wasm wasm_exec
all: wasm wasm_exec
wasm:
	GOOS=js GOARCH=wasm go build -trimpath -o takopi.wasm ./cmd/wasm

wasm_exec:
	cp "$(shell go env GOROOT)/misc/wasm/wasm_exec.js" .
```

この辺りの知見は[公式の wiki](https://github.com/golang/go/wiki/WebAssembly) にまとめられてました。

HTML などは少し書く必要がありました。長くなるのでここに [PR](https://github.com/Code-Hex/code-hex.github.io/pull/72) を置いておきます。

完成したものがこちらになります。

https://codehex.dev/takopi.html

これでブラウザで動くようになったので外出先でも安心してタコピーになれます。

## ユーザー体験の問題

ブラウザ上で動くようになりましたが、ロードが遅いのとブラウザアプリと Slack アプリを行き来するのが非常に大変でした。そこで [Slack Slash Commands API](https://api.slack.com/interactivity/slash-commands) を利用してコマンドを作成しました。

![ezgif-5-d9fb2581f5.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/118068/fc810612-1c5d-6b52-b567-b8770e10be6e.gif)

[ソースコード](https://github.com/Code-Hex/takopi-slack)です。

バックエンドに Cloud Functions を利用しました。Cloud Functions はこのような bot を作るのに非常にコストが低い印象があります。素晴らしい時代ですね。[料金プラン](https://cloud.google.com/functions/pricing)もご参考ください。

- [Slack のチュートリアル - Slash コマンド](https://cloud.google.com/functions/docs/tutorials/slack)
- [Cloud FunctionsとSlash_commandでSlackからConnpassの情報を取得する](https://qiita.com/daitasu/items/3c89485549afd2293a48)

Cloud Functions では Go 1.16 のランタイムが推奨になってます。注意しましょう。

これでどの環境でもタコピー風メッセージを簡単に投稿できるようになりました。


## 後日談

「個人的には `ッピ` 付いてると読むのに集中できないので素で書いて欲しい」とフィードバックをいただいたので、Slack に登録されている絵文字を多用する方向で頑張ることにしました🤩