SlackからClickupのタスクを作成したい


Clickup のタスク管理をしてくれるように gopher くんを育てました

もちろん goで作成しましたが、筆者は Rails で入門者レベルのチャットアプリを作ったことがあるくらいで、Go は競技プログラミングで使ってるだけです。初めての Go での開発!!

作成したアプリ

  • Slack から手軽に Clickup のタスクを作成したい
  • 公式の拡張機能は「リスト」を毎回選択する必要があるが、これがなかなか大変

なので、「Slack のチャンネルと Clickup のリストを 1 対1に紐づけて、メンションを飛ばすだけでタスク管理ができる」gopher くんを育てました。

こんな感じで gopher くんにメッセージを飛ばすと

タスクを作成して、リンクを返してくれます

全体の流れ

  1. Slack から @gopherにメンションを飛ばす
  2. メンションの内容から

    - 投稿元のチャンネル情報
    - 投稿主
    - タスクのタイトル
    - タスクの DueDate
    - タスクの asignee
    

    を取得する

  3. チャンネルと Clickup のリストが紐づいているかスプレッドシートを参照して、紐づいていれば 4.へ。
    そうでなければ、その旨をメッセージとして処理する

  4. 投稿主・asignee がそれぞれ登録されているか、スプレッドシートを参照する。登録されていれば 5.へ。
    そうでなければ、その旨をメッセージとして処理する

  5. Clickup api をたたいて、タスクを作成。response のリンクをメッセージとして処理する

  6. 処理したメッセージを Slack のスレッドに返す

開発の流れ

Slack を Go で扱う

まず、『Slack』を Go で扱うことができるのか?からスタートしました。
これは割と簡単に解決できました。
以下の参考記事を参照しました。
Slack ソケットモードの最も簡単な始め方
Slack ソケットモードの最も簡単な始め方 (Go 編)

これらの記事を参考に Slack アプリの設定して、コードをコピペして
go run main.goすると、とりあえず動いていることは確認できました。

サンプルコードをいじる

サンプルアプリがどのような機能を持っているのか熟読せずに、コピペしただけだったので、コードを読みました。
ここで、「何をトリガーにするのか」を考えながらコードを読んでいると

case *slackevents.AppMentionEvent:
  _, _, err := api.PostMessage(ev.Channel, slack.MsgOptionText("Yes, hello.", false))
  if err != nil {
    fmt.Printf("failed posting message: %v", err)
  }

「Menntion」とあったので、「たぶんここでメンションを飛ばしたときの処理を書いてるでしょ」と、予想しました。
'Yes, hello'を変更すると、なんか上手くいっていたので、ここに処理を書いて行けば良さそうだということを確認して開発を進めていきました。

Clickup api を使う

まず、タスクを取得

次に考えたのが、「Clickup を扱う」です。これは、公式のドキュメントを参照しました。
Clickup api
access-token や team-id の取得については以下のサイトを参考にしました。
clickup の API を使って、タスクの取得や更新をしてみる。

task のデータを扱うので、「Tasks」の部分に目を通しました。なんとなく、わかるけど・・・Go でどうやって書くのかわからない・・・
と悩んでいたら、なんだか言語ごとに書き方をのサンプルを掲載してくれていました。

環境変数の設定

Slack でも access token が必要でしたが、これらはコードに直書きせずに環境変数で管理しました。
環境変数の設定には Go で書かれた「direnv」を使いました。
direnv を使おう

環境変数の管理はこれしか使ったことがないですが、ディレクトリごとに設定できるのでとても便利です。
設定したものを、コードで活用する方法はググってパッと出てきたものを参考にして書きました。これはとても簡単でした。
[Go] 環境変数の取得/設定を行う

var (
    ACCESS_TOKEN = os.Getenv("CLICKUP_TOKEN")
    TEAMID       = os.Getenv("CLICKUP_TEAMID")
)

func ReadAllTasks() {
    client := &http.Client{}

    req, _ := http.NewRequest("GET", "https://api.clickup.com/api/v2/team/"+TEAMID+"/task", nil)

    req.Header.Add("Authorization", ACCESS_TOKEN)
    req.Header.Add("Content-Type", "application/json")

    resp, err := client.Do(req)

    if err != nil {
        fmt.Println("Errored when sending request to the server")
        return
    }

    defer resp.Body.Close()
    resp_body, _ := ioutil.ReadAll(resp.Body)

    fmt.Println(resp.Status)
    fmt.Println(string(resp_body))
}

この時点でこんな感じの関数を準備しました。
準備したところで「そもそもちゃんと api 叩けるのか?」が気になったので
Advanced rest client」という chrome の拡張機能を利用してきちんと使えるか確認しました。

とても簡単に扱うことができるのでおすすめです。

200 が返ってきたので、必要な情報はあることがわかりました。
あとは、上のコードを main.goに記述して実行してみるときちんと取得することができました。(スクショとるのを忘れた)

タスクを読み取ることができたので、あとはコードを少しいじって、作成できるようにしました。
これは公式を読めばなんとかなりました。

ここまでで、2日ほどかかりました^^;(週末潰れた)

Slack のチャンネルと Clickup のリストを1対1に紐付けする

Clickup のタスク作成ができれば、ほとんど出来上がっています。
次は、紐付けをどうするかを考えました。
DB 準備して、そこに保存することも考えましたが、社内ツールなので「だれでも使えること」を目標にスプレッドシートに保存して、それらを見に行く様にしました。(Go で DB 扱うようなことはしたことがなくて時間かかりそうだった)

以下の記事を参考にしました

こんな感じで紐付けしました

コードは以下

func CheckChannel(channelNum string) (bool, string) {
    b, err := ioutil.ReadFile("credentials.json")
    if err != nil {
    spreadsheetID := os.Getenv("SPREADSHEET_ID")

  // ここで範囲の指定してる
    readRange := "test1!B:C"
    resp, err := srv.Spreadsheets.Values.Get(spreadsheetID, readRange).Do()
    if err != nil {
        log.Fatalf("Unable to retrieve data from sheet: %v", err)
    }

    if len(resp.Values) == 0 {
        return false, "No, data"
    } else {
        for i := 0; i < len(resp.Values); i++ {
            if resp.Values[i][0] == channelNum {
                return true, resp.Values[i][1].(string)
            }
        }
        return false, channelNum
    }
}

channelNum は slack のメッセージを受信したときについてくる情報を放り込んでいます。
これで登録の有無によって、gopher くんのメッセージを変えることができるようになりました。
もし、登録されていなければ以下のように「Slack のチャンネル ID」を添えて、スプレッドシートに加えるよう教えてくれます

これで大方完成しました!
こんな感じで gopher くんにメッセージを飛ばすと

タスクを作成して、リンクを返してくれます

作成して

簡単なアプリだけど、4日くらいかかった。Clickup のドキュメント読むのが大変だった(eigo....)
スプレッドシートは公式を読めば実装できるかなって感じでした。
小さいアプリですが、ファイルを分けてパッケージも mainoutの二つに分けて実装しました。初めての静的型付け言語ですが、「今何を返す関数を実装しているのか」を意識しやすかったです。

ただ、宣言した変数はすべて使わないとコンパイルエラーが起きるのは「ちゅっと関数いじってテストしてみよう〜」がしにくい。。。

時間的には4日くらいですが、途中迷走して「neovim 入れちゃうぞ〜」とかパソコンを新調して「ターミナルのプロンプトかっこよくするぞー」とかやってたので、期間は1ヶ月くらいかかりました。このあたりの設定も備忘録として整理したいです。
dotfilesをとか作成してみたいです。(やりたいことが多すぎて時間が足りなすぎるな)

今年はもっと社内に gopher くんを布教していくぞ!!
そして今週はGo conferenceがありますね!!!!!!

参考記事

大変お世話になりました!

Slack ソケットモードの最も簡単な始め方
Slack ソケットモードの最も簡単な始め方 (Go 編)
Clickup api
clickup の API を使って、タスクの取得や更新をしてみる。
direnv を使おう
[Go] 環境変数の取得/設定を行う
Go でスプレッドシート情報を取得する
Advanced rest client
Sheets for DevelopersAPI v4