🔋

Goでslackのチャネルを開くAlfred Workflow作ってみた

2021/04/24に公開

Goの勉強がしたいと思い、普段使用しているAlfredのWorkflowを作ってみました。
今回は作ったものの紹介と公開するまでにやったことをまとめたいと思います。

Alfred Workflowとは

Alfredは多機能なランチャーアプリです。
無料版でも使えるのですが、有料版を購入することでWorkflow機能が使えるようになります。

Workflow機能は、コマンドを入力することでさまざまな作業を実行してくれる機能になります。Workflow機能が使えるようになると、自作したものや公開されているWorkflowが使えるようになり、さまざまなことができるようになります。

よく使われているものはまとめられています。

https://github.com/zenorocha/alfred-workflows

作ったAlfred Workflowの紹介

今回作ったWorkflowは、slackのチャネルを指定して、slackアプリでチャネルを開くという単純なものになります。

https://github.com/monda00/alfred-slack-workflow

機能

  • slackアプリでチャネルを開く

  • キャッシュのチャネル一覧を更新する

チャネルを開くとき毎回チャネル一覧を取得していると実行に時間がかかっていたので、キャッシュに保存しています。

Goで処理部分を作成する

ここからは実際に作成した部分を紹介します。

まずはGoで処理部分を実装していきます。
awgoというライブラリを使うことでAlfred Workflowを作成できます。

https://pkg.go.dev/github.com/deanishe/awgo

Goで実装する部分では以下のことをします。

  • チャネルを開くコマンドとキャッシュを更新するコマンドを分ける
  • slack-goを使ってチャネル一覧とチームIDを取得してキャッシュを更新
  • awgoで開くチャネルのアイテムを生成

slackアプリでチャネルを開くのはAlfred側で行います。

実行するコマンドを分ける

main.goではコマンドラインオプションでチャネルを開くコマンドとキャッシュを更新するコマンドを分けて実行できるようにします。

main.go
package main

import (
	"flag"

	aw "github.com/deanishe/awgo"
)

var (
	wf         *aw.Workflow
	cache_dir  = "./cache"
	cache_file = "cache.json"
)

type Channel struct {
	Name   string `json:"name"`
	ID     string `json:"id"`
	TeamID string `json:"teamid"`
}

func init() {
	wf = aw.New()
}

func run() {
	update := flag.Bool("update", false, "Update Channels")
	open := flag.Bool("open", false, "Open Channel")
	flag.Parse()

	if *update {
		updateChannels()
		return
	}

	if *open {
		openChannel()
		return
	}

}

func main() {
	wf.Run(run)
}

チャネル一覧とチームIDを取得

update.goではslack-goでチャネル一覧とチームIDを取得し、awgoのキャッシュ機能を使って保存しています。
slackのトークンはAlfredに環境変数があるため、そこに保存しています。

update.go
package main

import (
	aw "github.com/deanishe/awgo"
	"github.com/slack-go/slack"
)

func updateChannels() {
	wf.NewItem("Update Channels").Valid(true)

	c := aw.NewCache(cache_dir)
	cfg := aw.NewConfig()
	token := cfg.Get("SLACK_TOKEN")
	api := slack.New(token)
	params := slack.GetConversationsParameters{}
	channels, _, err_channels := api.GetConversations(&params)
	team, err_team := api.GetTeamInfo()

	if err_channels != nil || err_team != nil {
		wf.Warn("Error", "Error occurred in Slack API ")
	}

	all_channels := make([]Channel, 0)
	for _, channel := range channels {
		all_channels = append(all_channels, Channel{
			Name:   channel.Name,
			ID:     channel.ID,
			TeamID: team.ID,
		})
	}

	c.StoreJSON(cache_file, all_channels)
	wf.SendFeedback()
}

開くチャネルのアイテムを生成

キャッシュの情報から開くチャネルのアイテムを生成します。
Alfred側でチャネルを開くのにチャネルIDとチームIDが必要なため、Varメソッドで変数にして渡しています。

全てのチャネルのアイテムを生成したら、コマンドから与えられた文字列でフィルタリングしています。

open.go
package main

import (
	aw "github.com/deanishe/awgo"
)

func openChannel() {
	c := aw.NewCache(cache_dir)

	var c_channels []Channel
	if c.Exists(cache_file) {
		if err := c.LoadJSON(cache_file, &c_channels); err != nil {
			wf.FatalError(err)
		}

		for _, channel := range c_channels {
			wf.NewItem(channel.Name).
				Var("teamID", channel.TeamID).
				Var("channelID", channel.ID).
				Valid(true)
		}
	}

	args := wf.Args()
	if len(args) > 1 {
		wf.Filter(args[1])
	}

	wf.SendFeedback()
}

Alfred Workflowを作成する

次にAlfredからWorkflowを作成します。

Workflowの全体像は以下のようになります。

キャッシュの更新

Goの実行ファイルであるslack-alfred-workflow-updateオプションをつけて実行します。

チャネルを開く

-openオプションをつけて実行します。
{query}には実行する時のコマンドから開くチャネル名が渡されます。

実際にslackアプリでチャネルを開きます。
openコマンドを使うことでチャネル指定でslackアプリを開くことができます。

Makefileでビルドする

Goをビルドして、生成された実行ファイルとAlfred Workflowのinfo.plistファイルをzipでまとめてWorkflowファイルを作ります。

SHELL := /bin/bash

PLIST=info.plist
ICON=icon.png
EXEC_BIN=slack-alfred-workflow
DIST_FILE=slack.alfredworkflow
GO_SRCS=$(shell find -f . \( -name \*.go \))

all: $(DIST_FILE)

$(EXEC_BIN): $(GO_SRCS)
	go build -o $(EXEC_BIN)

$(DIST_FILE): $(EXEC_BIN) $(CREDITS) $(PLIST)
	zip -r $(DIST_FILE) $(PLIST) $(ICON) $(EXEC_BIN)

Github ActionsでCI/CDする

せっかくなのでGithub Actionsを使ってみました。
tagをpushすると、GithubのReleasesにWorkflowファイルがアップされるようにしています。

release.yml
name: Release
on:
  push:
    tags:
      - "v*"
jobs:
  release:
    runs-on: macos-latest
    steps:
      - name: Checkout source codes
        uses: actions/checkout@v2

      - name: Step Go environment
        uses: actions/setup-go@v2
        with:
          go-version: 1.16

      - name: Restore cache if available
        uses: actions/cache@v2
        with:
          path: ~/go/pkg/mod
          key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
          restore-keys: |
            ${{ runner.os }}-go-
      - name: Download modules
        if: steps.cache.outputs.cache-hit != 'true'
        run: go mod download

      - name: Build
        run: make

      - name: Create new release
        id: create_release
        uses: actions/create-release@v1
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        with:
          tag_name: ${{ github.ref }}
          release_name: Release ${{ github.ref }}
          draft: false
          prerelease: false

      - name: Upload release asset
        id: upload_release_asset
        uses: actions/upload-release-asset@v1
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        with:
          upload_url: ${{ steps.create_release.outputs.upload_url }}
          asset_path: ./slack.alfredworkflow
          asset_name: slack.alfredworkflow
          asset_content_type: application/zip

参考

Discussion