Google日本語入力で `mm/dd(a)` 形式の日付を予測変換の候補に出す

## 事の発端

**`きょう`** と入力した際の予測変換の候補に **`01/31(水)`** を表示したかった。

- Mac標準IMEの予測変換だと惜しい（下記左画像）
- 普段遣いのGoogle日本語入力だと曜日が含まれてない（下記右画像）

<div style="display: flex">
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/363036/b6653857-d867-ad33-0843-5ce74a882270.png" width=45%>
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/363036/31632e4d-f8f4-23d8-66fc-e28e927b80c2.png" width=45%>
</div>

期待した形式の日付に変換できなくて不便！（※）

というわけで、「 `きょう`, `あす`, `あさって` などの予測変換の候補に `mm/dd(a)` 形式の日付が出る」ようにした。

<div style="display: flex">
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/363036/889c2ed8-f8b3-5b4b-9a18-e087946e1629.png" width=45%>
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/363036/9d840e0e-53f2-1fa6-3da4-8f5f5750c4fe.png" width=45%>
</div>

（※真面目に調べれば、デフォルト状態 or 手軽な方法でこの形式の日付を予測変換に出してくれるIMEはあるかもしれない。というかありそう。）

## 実現方法

:::note warn
Google日本語入力の辞書ツールを使うため、他のIME利用者は別の方法で実現する必要があります。
AppleScriptを使うため、macOS以外を利用している方は別の方法で実現する必要があります。
:::

処理概要は下記の通りです。

1. PCの起動を検知する
1. 当日分処理が実行されてなければ実行する
1. Google日本語入力の辞書ツールを起動し、単語リストを書き換える

![デモ](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/363036/244097c9-439e-878f-bc2f-899c8219e748.gif)

上記GIF画像の処理が1日1回自動で走ります。

では、各処理の詳細について触れていきます。

### 1. PCの起動を検知する

こちらは `sleepwatcher` を用いて実現しました。

https://formulae.brew.sh/formula/sleepwatcher

macOSのスリープやスリープからの復帰を検知し、特定のスクリプトなどを実行してくれるツールです。

基本的な使い方は下記の通り。

```sh
# homebrewでインストール
$ brew install sleepwatcher

# スリープからの復帰時に実行したいスクリプトを `~/.wakeup` ファイルに記載（下記は一例）
$ echo 'sh ~/daily-dict-updater/wakeup.shell' >> ~/.wakeup

# `~/.wakeup` ファイルを実行可能にする
$ chmod +x ~/.wakeup

# sleepwatherを起動する
$ brew services start sleepwatcher

### 以下は補足 ###
# 起動状態の確認
$ brew services list
Name         Status  User     File
sleepwatcher started hogehoge ~/Library/LaunchAgents/homebrew.mxcl.sleepwatcher.plist

# 停止方法
$ brew services stop sleepwatcher

# sleepwatcherの使い方
$ brew info sleepwatcher
```

余談:
デフォルト設定では、sleepwatcherがスリープ時に呼び出すファイルは `~/.sleep`、復帰時に呼び出すファイルは `~/.wakeup` となっている。
これを変更するためには `brew services start sleepwatcher` を使わず、自前でplistファイルを作成してsleepwatcherを起動させれば良い……はず。
（plistの中身は `~/Library/LaunchAgents/homebrew.mxcl.sleepwatcher.plist` を参考にすれば良い）

### 2. 当日分処理が実行されてなければ実行する

日付の更新は1日1回で事足りるため、スリープ解除後に当日分処理が実行済みかどうかの確認を走らせます。
この実現方法は色々とありますが、今回はshellスクリプトで実現しています。

```sh:~/daily-dict-updater/wakeup.shell
#!/bin/sh

# 最新実行日時を記録するファイルのパス
LAST_EXEC_DATE_FILE_PATH=~/daily-dict-updater/last_exec_date.txt
# 明示的に日本語を指定することでタイムゾーンずれを防ぐ（べき論だとタイムゾーンを設定したほうが良いのか……？）
LANG=ja_JP.UTF-8

# ファイルが存在し、かつファイルに記載の日付が当日である場合は、処理を終了する
if [ -e $LAST_EXEC_DATE_FILE_PATH ] && [ `date +%Y%m%d` -eq `cat $LAST_EXEC_DATE_FILE_PATH` ]; then
    exit 0
fi
```

まだ当日分が実行されてない場合、後続処理に続きます。

```sh:~/daily-dict-updater/wakeup.shell
# AppleScriptのパス
APPLESCRIPT_FILE_PATH=~/daily-dict-updater/update_google_input_dictionary.scpt
osascript $APPLESCRIPT_FILE_PATH

# 処理が成功した場合、当日の日付をファイルに記録しておきます
echo `date +%Y%m%d` > $LAST_EXEC_DATE_FILE_PATH
```

上記で呼び出しているAppleScriptについては、次の説明に続きます。

### 3. Google日本語入力の辞書ツールを起動し、単語リストを書き換える

最後にメイン処理部分です……が、ここまでとは打って変わってわりと力技です。

AppleScriptを用いてアプリの立ち上げやUIの操作、キーボード入力を自動化しています。

```applescript:~/daily-dict-updater/update_google_input_dictionary.scpt
-- 引数に "きょう" を受け取ると今日の日付を "mm/dd(曜日)" 形式で返す関数
on get_date(yomi)
    if yomi = "きょう" then
        return (do shell script "LANG=ja_JP.UTF-8 date '+%m/%d(%a)'")
    end if
end get_date

-- メイン処理
tell application "System Events"
    delay 0.5

    -- デスクトップ画面に遷移する
    key code 103
    delay 0.5

    -- カーソル位置を初期化するため、Google日本語入力のユーザ辞書を閉じている状態から始める
    tell application "DictionaryTool" to quit
    delay 0.5

    -- ユーザ辞書を開く
    tell application "DictionaryTool" to activate
    delay 0.5

    -- "u" から始まる名前のユーザ辞書を選択する（あらかじめ `u_hogehoge` という名前の辞書を登録しておく）
    keystroke "u"

    -- 単語を編集できる位置にカーソル移動する
    keystroke tab & tab
    keystroke return

    -- keystrokeで単語入力ができないので、クリップボードを介して辞書の "よみ" 欄に `きょう` を設定する
    delay 0.5
    set the clipboard to "きょう"
    keystroke "v" using {command down}

    -- 同様に "単語" 欄に `mm/dd(a)` 形式の日付を設定する
    delay 0.5
    set the clipboard to my get_date("きょう")
    keystroke tab
    keystroke "v" using {command down}
end tell
```

コメントに記載した通り、キーボードでできるUI操作をAppleScriptで自動化する、という力技になっています……
Google日本語入力の辞書ファイルも存在したのですが、SQLiteなどのわかりやすい形式ではなく独自形式（？）の模様だったので、このようにUI経由で単語登録をする方法になりました :innocent: 
（~~アプデでUI変更あったら動かなくなる未来~~）

## さいごに

荒削りですが、個人的にはめっちゃ便利。

実際に使っているスクリプトは下記リポジトリで公開しています。
（開発途中で不備・不具合などありますが、そこはご愛嬌ということで）

https://github.com/c6tower/mac-utils-scripts/tree/main/daily-dict-updater

ちなみに、本記事では取り上げていない問題点やら、試したけど上手くいかなかったこと、検証中のことなど色々とあるので、その辺はまた別の機会に記事化します。（たぶん）

~~それにしても、もっとスマートな実現方法がある気がしてならない。~~

## 参考記事

https://support.apple.com/ja-jp/guide/terminal/trml1003/mac

https://www.dev-dev.net/entry/2021/03/24/164931