きょこみのーと

元六本木でGo書いてました。今はVRでGo書いてます。

AnnictのAndroidアプリを久しぶりに機能追加などした

はじめに

約2年くらい前につくって、作りっぱなしだったAnnictterを長い時を経て改修しました。

(作ったときの記事は以下)

kyokomi.hatenablog.com

実は自分は作ってからずっと利用しているヘビーユーザーだったのですが、Androidのバージョンも10に上がって久しぶりに開発したくなったので色々足りてない機能を追加しようと決意し、とりあえずそこそこ機能追加してリリースし、今に至ります。

play.google.com

今回の記事で話すこと

技術的なことやアプリの内容についてなど結構色々話したいこともあり長くなりそうなので、技術的な話は別記事をあとで書きます。今回話すことは以下です。

  • 結局どういう風に利用していたか?
  • 不満とか改善したかった点
  • 今回改修した内容
  • 今後の予定

結局どういう風に利用していたか?

f:id:kyokomi:20191112213147p:plain
リリース時のblogより

とのことだが、こんな感じでした。

  • リストを同じ作品の複数話で埋めてしまい、途中で視聴を一時中断や視聴中止したくなる(これは想定どおり)
    • 一時中断や視聴中止したいが、ブラウザを起動してログイン -> 視聴中の一覧から探す -> ステータスを変更する のが面倒だった(なんかひと手間を感じてしまい、あとでまとめてやろっと〜って思ってしまいズルズルと...)
  • 結局四半期に1回棚卸し的な感じで来期のアニメをチェックしながら、今期はこれ途中で離脱してしまったな〜とかを振り返りながら、まとめてステータスを更新することになっていた(これはこれでいいんですが...)

f:id:kyokomi:20191112213737p:plain:w200
実質視聴が一時中断してる様子
(これはまだ視聴再開の見込みがある方、1話〜10話くらいまで全く見ないで並んでることもあった)

  • 結果、見たい -> 視聴中止見てる -> 視聴中止 をあとでまとめてステータス更新する運用になってしまい、 一時中断 を使いこなせていなかった。

不満とか改善したかった点

  • 一時中断のステータスをもっと活用して、やっぱ続きを見ようという流れを作りたい(思考停止して過去の名作に手を伸ばしがちなので...)
  • 来期これくるのか〜ってのをもっと早い段階で知って、準備(?)したい
  • 未視聴の話リストは、常に空リストになる感じの運用にしたい(一生タスクが整理できてない感)

今回改修した内容

作品一覧の表示・ステータス更新画面を追加

現在時刻を元に、前期、今期、来期をタブレイアウトで表示。 また、作品のステータスを一覧で確認できて、そこから編集できるようにしました。

f:id:kyokomi:20191112215137p:plain:w200f:id:kyokomi:20191112215300p:plain:w200
作品の一覧とステータス更新

BottomNavigationを追加し、視聴記録画面 -> 今期作品一覧を行き来しやすく

とりあえずサッと作品視聴ステータスを更新して、すぐに視聴記録の一覧に反映されるというのが最高です。

f:id:kyokomi:20191112220247g:plain:w250
ステータス更新して、視聴記録一覧へ反映

今後の予定

基本的に自分のAnnictの用途がスマホで、完結できるようにするのが目標です。

作品のチャンネル設定できるようにする

実はこれをちゃんとやらないと、未視聴話の一覧に反映されません。基本的には自動で設定されるの大体大丈夫なのですが、たまにWebサービスの視聴が設定されてしまい放送予定が未定だと一覧出てこないという状況になります。

f:id:kyokomi:20191112220727p:plain:w300
Webのチャンネル一覧
https://annict.jp/channel/works

見たい、見てるの一覧を見ながら以下を設定できる画面を実装する

  • 実は見なかった(見たい -> 未設定)
  • 数話みたが、もう見なさそう(見てる -> 視聴中止)
  • 今期〜来期の狭間で見るかも?(見たい・見てる -> 一時中断)

視聴記録時に「Twitterに投稿する」を選択できるようにする

Twitter見てる感じ、最近アニメあんまり見てない?」って昔からのアニメ仲間に言われたので、そんなことないよ!ってアピールをできるように(笑)

これはすぐ実装終わりそうなのでさっさとやる。誤爆しないようなUIにするのだけ注意する。

おわり

次回は、技術面について掘り下げて話していければと。 Androidアプリ開発久しぶりすぎて色々学びが多かったので。

では、みなさん良いアニメライフを!!!!

goa v3をそろそろ検証してみる

はじめに

github.com

今回は以下について

  • とりあえず触ってみた & 感想
  • Middlewareについて調査
  • Panic時の挙動とPanicをMiddlewareでハンドリングする

とりあえずGetting Startedを参考に触ってみた

https://goa.design/learn/getting-started/ 見ながらやっていく。

スッと動いた。良き!!

 [calcapi] 16:16:15 HTTP "Add" mounted on GET /add/{a}/{b}
 [calcapi] 16:16:15 HTTP "./gen/http/openapi.json" mounted on GET /openapi.json
 [calcapi] 16:16:15 serving gRPC method calc.Calc/Add
 [calcapi] 16:16:15 HTTP server listening on "localhost:8000"
 [calcapi] 16:16:15 gRPC server listening on "localhost:8080"
 [calcapi] 16:16:17  id=sGdiU14f req=GET /add/1/2 from=127.0.0.1

generateされたclientもあるけど、あえてcurlで叩いてみる。

 curl -i -X GET http://localhost:8000/add/1/2
 HTTP/1.1 200 OK
 Content-Type: application/json
 Date: Sun, 20 Oct 2019 07:20:22 GMT
 Content-Length: 2
 
 3

よさそう

v1 -> v3の移行手順はこれらしい

https://goa.design/learn/upgrading/

designは基本的には、これにしたがって移行していけばよさそう。

また今度実際に移行したら色々書く予定

midlewareについて

goa.design

gRPCに対応したことで、プロトコル関係なく実行するmiddlewareとプロトコル単位で設定できるmiddlewareの2種類になったようす。

Endpoint Middleware

Useを使えば今まで通りすべてのpathに対してのmiddlewareになるみたい。 calcEndpoints.Add に対してだけ設定するみたいなやりかたもあるみたい。

calc/main.go.diff

     // Wrap the services in endpoints that can be invoked from other services
  // potentially running in different processes.
  var (
      calcEndpoints *calc.Endpoints
  )
  {
      calcEndpoints = calc.NewEndpoints(calcSvc)
 
      // Apply ErrorLogger to all endpoints.
      calcEndpoints.Use(middleware.ErrorLogger(logger, "calc"))
 
      // Or apply ErrorLogger to specific endpoint.
      //calcEndpoints.Add = middleware.ErrorLogger(logger, "add")(calcEndpoints.Add)
  }

Transport MIddleware

HTTPだけgRPCだけ実行みたいなのがやりたいときに使うぽい。

calc/middleware/auth.go

 package middleware
 
 import (
  "context"
  "net/http"
  "strings"
 )
 
 // WithAuthToken is a HTTP server middleware that reads the value of the
 // Authorization header and if present writes it in the request context.
 func WithAuthToken() func(http.Handler) http.Handler {
  return func(h http.Handler) http.Handler {
      // A HTTP handler is a function.
      return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
          req := r
          // Grab Authorization header and initialize request context with it.
          if bearerToken := r.Header.Get("Authorization"); bearerToken != "" {
              ctx := context.WithValue(r.Context(), AuthTokenKey, strings.Replace(bearerToken, "Bearer ", "", 1))
              req = r.WithContext(ctx)
          }
 
          // Call initial handler.
          h.ServeHTTP(w, req)
      })
  }
 }

goa.Context じゃなくて普通の context.Context がくるのでそこからGetすればOK

calc.go

 s.logger.Print("Token = ", ctx.Value(middleware.AuthTokenKey))

PanicHandler

httpパッケージのserver.goのserveメソッド側のrecoverでhandlingされる。

 2019/10/20 21:16:16 http: panic serving 127.0.0.1:57525: test
 goroutine 21 [running]:
 net/http.(*conn).serve.func1(0xc00017e280)
         /usr/local/opt/go/libexec/src/net/http/server.go:1767 +0x139
 panic(0x1507000, 0x168e4c0)
         /usr/local/opt/go/libexec/src/runtime/panic.go:679 +0x1b2
 calc.(*calcsrvc).Add(0xc0000b0550, 0x16a6860, 0xc0001d43f0, 0xc0001d2130, 0xc0001ec101, 0xc0001d2130, 0xc0000eb760)
         /Users/kyokomi/workspace/ghq/github.com/kyokomi-sandbox/sandbox/golang/goav3/calc/calc.go:29 +0x11c

これはこれでとりあえず助かるんですが、responseも以下みたいな感じになってしまい...

共通の500エラーとか返したいなぁと...

 $ curl -i -X GET -H "Authorization: Bearer hogehogehogehoge" http://localhost:8000/add/1/2
 curl: (52) Empty reply from server

v1のようにEndpoint Middlewareでrecoverを入れてみる

注意点としては、recoverしたときにreturnのerrorをちゃんとdefer内で差し替えないと、正常処理に入ってしまいそっちで別のpanic等が起きてしまうことがあります(encodeとかdecodeのあたりで)

calc/middleware/panic_handler.go

 package middleware

 import (
  "context"
  "fmt"
  "log"
 
  goa "goa.design/goa/v3/pkg"
 )
 
 // PanicRecover panic recover middleware
 func PanicRecover(l *log.Logger) func(goa.Endpoint) goa.Endpoint {
  return func(e goa.Endpoint) goa.Endpoint {
      return func(ctx context.Context, req interface{}) (response interface{}, err error) {
          defer func() {
              if exception := recover(); exception != nil {
                  panicErr, ok := exception.(error)
                  if !ok {
                      panicErr = fmt.Errorf("recover error %v", exception)
                  }
                  l.Printf("[PANIC] %v", panicErr)
                  err = panicErr
              }
          }()
 
          response, err = e(ctx, req)
          return
      }
  }
 }

適当にhandlerでpanicさせてときのログ

 [calcapi] 21:19:33 calc.add
 [calcapi] 21:19:33 Token = hogehogehogehoge
 [calcapi] 21:19:33 [PANIC] recover error test
 [calcapi] 21:19:33 [ERROR] calc: recover error test
 [calcapi] 21:19:33  id=IGTAuYo3 status=500 bytes=111 time=247.777µs

curlの結果もこんな感じになる

$ curl -i -X GET -H "Authorization: Bearer hogehogehogehoge" http://localhost:8000/add/1/2
HTTP/1.1 500 Internal Server Error
Content-Type: application/json
Date: Sun, 20 Oct 2019 12:19:33 GMT
Content-Length: 111

{"name":"fault","id":"Aghc9GSY","message":"recover error test","temporary":false,"timeout":false,"fault":true}

Go言語による並行処理

Go言語による並行処理

Go言語による並行処理

感想だけシュッと書きます。

  • 今までなんとなく使ってたgoroutineやcontextのCancelなどを改めて学ぶことができてよかった
  • いつも、 sync.WaitGroup で事足りてしまい、あまりチャネルを使えてなかったなぁという学びもあった
  • goroutineでアクセスする系で、気軽にsync.RWMutex を使いがちだったなぁと反省した

とはいえ、じゃあ毎回チャネルを使うのか?というのも正解ではなく、使うことによってパフォーマンスや処理が最適化されるが、コードの可読性などがトレードオフになりえる点などを気をつけて、書籍の例にあるように書き込みと読み込みの責務をしっかり分けるなどやっていければと思いました。

monorepoでCircleCIを使ってherokuへのdeployをする

はじめに

かなり特殊な構成なので自分以外には役に立たないかもしれませんが...

個人開発のプロジェクトをmonorepoで開発しているのですが、apiサーバーがherokuなので単純にmasterをそのままherokuにpushするとめちゃ不要なファイルなどが含まれてしまうという問題がありました。

gitignoreすればいいとは思いつつも、heroku用のgitignoreとgithubで管理するコード用のgitignoreみたいなことをするのも辛いので、 git worktree を使ってheroku用のブランチへのpushを行い、それをherokuにもpushするというアプローチにしました。(以下の図のようなイメージ)

herokuまでのdeployの流れ

f:id:kyokomi:20191019212317p:plain
herokuまでのdeployの流れ

CircleCIの各jobの説明

  • build jobは普通にgolangのbuildやtestなどを行う
  • git_push jobは deploy.sh を実行して、 heroku用のworktreeを作成し github にpushします
    • deploy.sh の中から呼び出している deploy_generate_file.sh で必要なファイルだけをコピーするようにしてます
    • githubheroku ブランチへapiサーバーのdeployに必要なファイルだけがpushされ、またCircleCIが呼び出されます
  •   一度ここでCircleCIのworkflowは途切れてしまいます
  • heroku_deploy jobによって herokuのgitリポジトリに対してpushを行います
    • このpushでheroku側でdeployが行われます

登場するファイル(参考までに

deploy.sh

#!/bin/bash

set -x

# shellcheck disable=SC2006
if [ "`git status -s`" ]
then
    echo "The working directory is dirty. Please commit any pending changes."
    exit 1;
fi

echo "Deleting old publication"
rm -rf heroku
mkdir heroku
git worktree prune
rm -rf .git/worktrees/heroku/

echo "Checking out heroku branch into heroku"
git worktree add -B heroku heroku origin/heroku

echo "Removing existing files"
rm -rf heroku/*

echo "Generating site"
sh ./deploy_generate_file.sh

echo "Updating heroku branch"
cd heroku && git add --all && git commit -m "Publishing to heroku (deploy.sh)"

echo "Pushing to github"
git push -u origin heroku

deploy_generate_file.sh

#!/bin/bash

set -x

cp -r .circleci heroku       # circleCIが動くようにするため
cp -r api heroku             # このディレクトリにapiサーバーのコードが入ってます(main.goとかとか)
cp go.mod go.sum heroku      # 訳あってgo.modが上位階層にあるのでそれもコピー
cp app.json Procfile heroku  # herokuのdeployに必要なファイル
cd heroku && go mod vendor   # heroku側でfetchしなくていいように(deployを早くするため)にgo mod vendor

circleci/config.yml

# Golang CircleCI 2.1 configuration file
version: 2.1

jobs:
  build:
    docker:
      - image: circleci/golang:1.13
    environment:
      GO111MODULE:     "on"
    working_directory: /go/src/github.com/kyokomi/webapp-sandbox
    steps:
      - checkout
      - run: go get -u golang.org/x/lint/golint
      - run: go get -u golang.org/x/tools/cmd/goimports
      - run: make test_all
      - run: make build_api

  git_push:
    docker:
      - image: circleci/golang:1.13
    environment:
      GO111MODULE:     "on"
    working_directory: /go/src/github.com/kyokomi/webapp-sandbox
    steps:
      - checkout
      - run:
          name: git settings
          command: |
            git config --global user.email kyokomidev@gmail.com
            git config --global user.name circleci

      - run: sh deploy.sh

  heroku_deploy:
    docker:
      - image: buildpack-deps:trusty
    environment:
        HEROKU_APP_NAME: "kyokomi-webapp-sandbox"
    steps:
      - checkout
      - run:
          name: Deploy Master to Heroku
          command: |
            git push https://heroku:$HEROKU_API_KEY@git.heroku.com/$HEROKU_APP_NAME.git master

workflows:
  api_build_and_deploy:
    jobs:
      - build:
          filters:
            branches:
              ignore: heroku
      - git_push:
          requires:
            - build
          filters:
            branches:
              only:
                - master
      - heroku_deploy:
          filters:
            branches:
              only:
                - heroku

ISUCON9予選にk02で参加して完全敗北しました #isucon

いつものメンバーでISUCONに参加して完全敗北...! いつもどおりGo書く人を担当。 最大スコアは3,120で、最終結果は fail 。

以下メンバーの感想。

alligatorswamp.hatenablog.com

事前にやったこと

  • scrapboxに色々使いそうな記事をまとめたり
  • ISUCON8予選の過去問の環境を構築して模擬したり
  • 使うツールの選定など

ISUCON9予選当日の作業の流れ(kyokomi目線)

  • 10:00 開始
    • ドキュメントを読む -> 読んでる途中にAlibabaでの環境構築を先にやったほうが良いと判断
  • 10:15 〜 10:15
    • Alibabaの環境構築完了
    • sshできることと、アプリケーションにログインできることを確認した https://gyazo.com/b8705539ea9092849d86721548d54399
    • 速攻でベンチマークを実行する image
    • ベンチ実行によってクレジットカードの認証が通らなくなったことに気が付かずメンバーの二人がわちゃわちゃしてた
      • しばらくjsのコードとか見た後にレギュレーションに書いてあったこと気がつく...
  • 10:00 〜 10:30
    • githubにpushする
    • scrapboxにメモってた手順のpush先が事前に素振りしたときのrepositoryになってて無駄に混乱した
    • image
    • github tokenでシュッとpushできるようにする手順書いてたのは正解だった
  • 10:30 〜 10:50
    • ファイル構成把握しながら、不要なファイル削除したり、gitignoreの設定を追加
    • ローカルで起動するようにflag追加、docker-composeの追加など
    • 初期データがローカルでなくて動かないことに気が付き120MBもあることからgitignoreされてるというやさしさに気がつく
  • 10:50 〜 11:30
    • テーブル構成を把握したかったのでER図作ろうとして外部キー貼ったら、初期処理でエラーになって「う〜ん?」って調べてたが本質ではないな...と30分くらい無駄にしてホワイトボードに手書きした
  • 11:30 〜 12:10
    • main.goのコードが長く、目的のコードがさっと見つけられず大まかな機能単位でfileを分けるリファクタに手をだした
    • 見通しはよくなったし改修時の対象を探すのも楽になった感じはある
  • 12:10 〜 12:30
    • Prometheusで監視できるようにmetricsのhandlerを仕込んだり
    • image
    • 結局ほとんど使わなかった...
  • 12:30 〜 13:30
    • マイページのtransactionsの取得がボトルネックぽいということで見始める
    • `status IN (?,?,?,?,?) AND ` がサクッと見て無駄そうなので消した(全ステータス指定してたので)
    • indexを貼った。queryの負荷はそこそこ減ったぽい(スロークエリ曰く) image
  • 13:30 〜 14:00
    • categoriesが32万回くらいselectされてたのでサクッとcacheした
    • 100〜1000回くらい?まではselect回数が減った(レコード数は40くらいなんだけど)
      • これはinitで全レコードcacheしたほうがよかったなとあとから思った
      • 一旦、できるだけ既存の実装から離れないように〜というポリシーでこうしてた
  • 14:00 〜 14:40
    • usersもcacheした
    • この時点の3120点になってた(そしてこれが最終的に最大スコア...)
  • 14:40 〜 15:00
    • なんかスコア上がらねーなと思い、このままgetTransactionを見ていっていいのか???と不安になり
    • キャンペーンを有効にしてみる?という話になって1に設定した
    • 結果なんかめっちゃエラーになる
    • そしてAPIGatewayErrorとtoo many connectionsがでまくる
    • DBのCPU使用率も落ち着く(これはミスリード...)
    • これを解消するのが鍵では!!!!!と作戦を変更(これが失敗だった...)
    • MaxConectionはアプリ側は一旦120に設定し、DB側(3号機)で300くらいに設定して一旦でなくなった
    • APIGatewayの502エラーが自分たちのNginx?と勘違いしてulimitか!!とか結構時間無駄にした
      • よく考えたらアプリログでNginxのログでるのおかしくね???ってなり、そこでようやく外部サービスやん...って気がつく
  • 15:00 〜 17:00
    • とりあえずログやmeasureでどれくらい外部APIが叩かれてるのかを可視化したり
    • statusの取得が無駄っぽいから wait_done 以外は shippings のテーブルの結果を見てresponseに設定したりを入れてリクエストを減らしたり
    • item購入時にreserveするの不要そう?(実際にshipするときでいいのでは)と判断し、shippingsテーブルのreserve_idとかをNULL許可にしてうまいことshipするやつだけreserveの外部APIを叩くようにしてリクエストを減らしたり image
  • 17:00 〜 18:00 終了
    • ずっとキャンペーン有効による外部APIのGatewayTimeoutをなんとかせねば〜と試行錯誤していてFailしたままなのでなんとかしなければと焦り始める
    • 502エラー速攻で返すくらいならタイムアウトギリギリまでRetryしたほうがいいのでは?と、適当に1秒sleepして3回までRetryするbackoff的な実装を強引にGOTO文で実装したりしたがしかし...
    • 結局最後は10回Retryしてタイムアウトになり、じゃあ5回だ!!とか完全にヤケクソ状態の山賊状態だった...
    • 10ってコメントのpushなのにコードは10 -> 5に変更っていう...もうむちゃくちゃ...
    • もう何と戦ってるか本人達もわかっていないまま試合終了してしまった... 〜完〜

まとめ

  • 想像を上回る良い出題ですごく楽しかったです
  • レギュレーション大事だよな...!って毎回わかってるつもりだけど終わるまで色々気づけてなくて悔しい
  • なんかいっぱいAPI叩かれてるね。なんかめっちゃCPU上がってるね。の深堀りができる状況になってなかった
  • 悔しい...!来年こそは...!

ISUCON8予選を突破しました!!!!!!!

k02というチームでISUCON8予選に参加し、予選を無事通過して本戦出場が確定しました。

感想と色々と良かった点や反省点をまとめようと思います。

やったこと

主にgolangの実装を改修を担当してた。

  • githubやgitまわりの設定
  • N+1の対応を何箇所か対応
  • reservationsテーブルでFOR UPDATEしてない箇所の対応
  • ORDER BY RAND() LIMIT 1してるやつをリストで取得して、golang側でシャッフルして1件選ぶ実装
  • 無駄にeventsやsheetsを取得してる箇所を修正
  • reservationsテーブルにuser_idのindexを追加
  • Adminのreportのsqlを @yasu が改善してくれてそれを組み込んだ

良かった点

  • 事前にk02用のkibelaを用意した
    • 当日の作業ログとかに埋もれてしまうpassやip、deployコマンドなどなどをslackを辿らずに調べられるようにするため
  • 横並びで全員外部モニタがある状態の会場を用意した
    • コミュニケーションコストを下げた
  • 序盤にちゃんとサービス触って理解する時間をとっていた
    • これによって途中で怪しい挙動してるときにrevertする決断が早かった
    • Lock周りの挙動がイメージしやすくなった
  • DataGripでER図をさっと作成していつでもデータの関連を見れるようにした
  • h2oはなんかパッと見そのままでも問題なさそうと @waniji が判断し、ひたすらDatabaseのチューニングに全力を注いだ
  • 流石に4回目だからか山賊してもスコア上がらないのはみんな理解してて冷静に状況判断できてたのかなと思いました
  • 去年は一発逆転でめちゃくちゃして結果Failしてたので、Failが発生しないように安定するのに注力できたのは良かった

kibe.la

f:id:kyokomi:20180918113212p:plain

反省点

  • チームの作業担当がふわっとしていて、後半までみんなウズウズしていた
  • nginxかなーと思って用意してたgoaccessとかが使えなかった
  • 自分以外がほとんどコードに手を付けられなかった(golang慣れしてて一か八かdeployを繰り返してた)
    • これは手元で動作する環境をさっさと用意しなかったのが一番の原因
  • sqlxの導入やちょっとしたリファクタリングができず終始しんどいコードを読んでた
    • これも手元で動作する環境さっさと用意すれば、悩みながらリファクタしていけて結果的にコスパ良かったと思う

以下とかもさっさとsqlx入れれば何回か実行エラーになってたタイムロスしたのを回避できたはず...

感想

今回は、飛び道具よりも愚直にSQLを改善したりindexを貼っていくのが大事な題材だったな〜と思いました。

普段仕事でやってるようなSQLをこねくり回して実行計画をみたりER図みたり、サービスの挙動としてあるべき姿をイメージしてみたいな感じでとても良かったです。

alfred-esa-workflowに検索結果から正規表現で文字列を抽出するコマンドを追加した

github.com

昔作ったこれに久しぶりに機能を追加しました。

何故作ったか?

日報に 良かったこと/悪かったこと/所感 などを毎日書いていて、2週間ごとの振り返りで漁ったりしてたんですが、いい感じに抽出したいな〜というモチベーションから機能追加しました。

使い方

※日報に記載した 悪かったこと の内容を抽出する例になってます

f:id:kyokomi:20180128175501p:plain

抽出した文字列(クリップボードにコピーされます)

## general/日報/2018/01/12/kyokomi
- ピザ食べすぎた...


## general/日報/2018/01/11/kyokomi
- とくになし


## general/日報/2018/01/10/kyokomi
- とくになし


## general/日報/2018/01/09/kyokomi
- 休み明けで体がまだなまっていて眠い... :zzz: 

....続く

設定変更方法

日報のテンプレートがウチは違うんだよな〜という方もご安心ください。

Show Alfred Preferencesを開いて、以下のRun scriptの引数を変更してください。

f:id:kyokomi:20180128180233p:plain

f:id:kyokomi:20180128175612p:plain

./alfred-esa-workflow regexp 以降の引数は、以下の内容になります。

  • ユーザ名(必須): "{query}"
  • 検索キーワード(必須): "日報"
  • 検索結果から文字列を抽出する正規表現(必須): "# 悪かったこと([\s\S]*)# 所感"
  • 抽出結果のprefixにつけた文字列: "## "

カスタマイズ

基本的に自分の振り返り用なのでいちいち引数に自分のusername設定するのめんどいという方は、こんな感じに改造すればさらに便利になります。

f:id:kyokomi:20180128180949p:plain

f:id:kyokomi:20180128181002p:plain

おわり

ちなみに制御が面倒だったので1回のGETリクエストで取得できるデフォルトの件数(20件)しか取得しないようになってます。

需要があったら改修しますのでご連絡もしくはPullRequestお待ちしてます( ^ω^)