macOSでテキストを自動送信
する4つの方法を比較してみた
(ChatSpooler開発記録)

macOS向けのチャット自動送信ツールを作っていて、テキストの送信方法をどうするか悩んでいました。AppleScript、pyautogui、clipboard、CGEventと4つの選択肢があって、それぞれメリット・デメリットがあります。

関連記事

1. 最初はAppleScriptから始めた

最初に試したのはAppleScriptでした。macOSの標準技術だし、安定してそうだなと思ったんです。

tell application "Claude" to activate
tell application "System Events"
    key code 9 using command down  # Cmd+V
    delay 0.3
    key code 36 using command down # Cmd+Enter
end tell
Code language: PHP (php)

実装自体は簡単でした。アプリを指定して、キーコードを送信するだけ。確かに動きます。

でも問題がありました。

1.1. アクセシビリティ権限が必要

System Eventsでキー操作を送信するには、アクセシビリティ権限が必要なんです。システム設定を開いて、手動で許可する必要があります。

システム設定 > プライバシーとセキュリティ > アクセシビリティ

初心者には結構ハードルが高いですよね。しかもエラーメッセージが「osascriptにはキー操作の送信は許可されません」とか出るんですが、これを見て何をすればいいか分かる人は少ないと思います。

それに、もっと気になることがありました。

1.2. 送信中にキーボードが使えない問題

AppleScriptで送信している間、自分のキーボードやマウスが使えなくなるんです。送信処理が終わるまで、他の作業ができません。

これは地味にストレスでした。

「もっと良い方法はないかな?」と思って、次に試したのがpyautoguiです。

2. pyautoguiはシンプルで良かった

pyautoguiはPythonのライブラリで、キーボード操作をシミュレートできます。

import pyautogui

pyautogui.hotkey('command', 'v')
time.sleep(0.3)
pyautogui.hotkey('command', 'return')
Code language: JavaScript (javascript)

実装がすごくシンプル。コードを見れば何をしているか一目瞭然です。

それに、アクセシビリティ権限が不要でした。これは大きなメリットですね。

2.1. でもやっぱり操作がブロックされる

pyautoguiも結局、送信中はキーボードが使えませんでした。これはAppleScriptと同じ問題です。

送信中に他の作業をしようとすると、変なところにテキストが入力されたりします。送信が完了するまで、じっと待つしかありません。

「ユーザーの操作に干渉しない方法はないのかな?」

そう思って調べていたら、CGEventという方法を見つけました。

3. CGEventとの出会い

CGEventは、macOSのシステムレベルでイベントを送信する仕組みです。pyobjcというライブラリを使います。

最初は「難しそうだな」と思ったんですが、実際に試してみると驚きました。

from Quartz import CGEventCreateKeyboardEvent, CGEventPost

# キーダウンイベント
event_down = CGEventCreateKeyboardEvent(None, keycode, True)
CGEventSetFlags(event_down, kCGEventFlagMaskCommand)
CGEventPost(kCGHIDEventTap, event_down)

# キーアップイベント
event_up = CGEventCreateKeyboardEvent(None, keycode, False)
CGEventPost(kCGHIDEventTap, event_up)
Code language: PHP (php)

3.1. 送信中も自由に操作できる

CGEventで送信している間、普通にキーボードやマウスが使えるんです。

これにはちょっと感動しました。送信処理が走っているのに、別のウィンドウで作業できる。メールを書いたり、ブラウザを見たり、全く問題ありません。

なぜこんなことができるのか調べてみました。

4. なぜCGEventは干渉しないのか

違いはイベントを送る「レベル」にあります。

AppleScriptとpyautoguiは、アプリケーション層やGUI層で動作します。つまり、実際のキーボード入力をシミュレートしているんです。だから、ユーザーが同時にキーボードを使うと競合してしまいます。

一方、CGEventはシステムカーネル層で動作します。システムに直接「このキーが押されたことにして」と指示を出す感じです。ユーザーの実際のキーボード入力とは別の経路なので、干渉しません。

例えるなら、AppleScriptとpyautoguiは「ロボットの手でキーボードを叩く」方法です。人間の手とぶつかります。

CGEventは「キーボードを経由せずに、直接コンピュータの脳に指令を送る」方法です。手がぶつかりません。

なるほど、と思いました。

5. 操作への干渉と権限の必要性

4つの方法を実際に試した結果を表にまとめてみます。

方式送信中の操作アクセシビリティ権限実装の難易度依存ライブラリ
AppleScriptブロックされる必要中程度。スクリプトの文法に慣れが必要。
pyautoguiブロックされる不要一番簡単。pyautogui
(軽量で、数MB)
clipboardブロックされる必要(AppleScript使用時)結局AppleScriptかpyautoguiを使うので、独自のメリットはあまりありません。
CGEvent自由に使える不要中程度。キーコードの理解が必要pyobjc
(約50MBでそこそこ大きい)

pyautoguiの方が、実装が簡単で、すぐ動きます。権限設定も不要です。送信中に他の作業をしたいという要求がなければ、これで十分だと思います。

CGEventは権限が不要なのも嬉しいポイントです。pyobjcがある環境では干渉なしで動作します。

AppleScriptとclipboard方式は、もう使わないと思います。AppleScriptは古い技術で、権限も必要。clipboard方式は、結局AppleScriptかpyautoguiを使うので、独自のメリットがありません。

5.1. ハイブリッド方式という解決策

「pyobjcが入っていない環境ではどうするか?」という問題がありました。

そこで考えたのが、ハイブリッド方式です。

class HybridSender:
    def __init__(self):
        try:
            from Quartz import CGEventCreateKeyboardEvent
            self.method = "CGEvent"
            print("CGEvent方式を使用(干渉なし)")
        except ImportError:
            self.method = "pyautogui"
            print("pyautogui方式を使用(フォールバック)")

pyobjcがあればCGEventを使う。なければpyautoguiにフォールバックする。

これなら、どんな環境でも動作します。そして、pyobjcがある環境では最高のユーザー体験を提供できます。

6. CGEventは思ったより簡単

最初は「難しそう」と思っていたCGEventですが、実際に書いてみると、そこまで複雑ではありませんでした。

キーコードさえ分かれば、あとは定型的なコードです。

KEY_V = 0x09        # V
KEY_RETURN = 0x24   # Return
Code language: PHP (php)

macOSのキーコード一覧を見れば、必要なキーはすぐ分かります。

送信前のクリップボードとアクティブアプリを保存して、送信後に復元する処理も追加しました。

これがないと、ユーザーがコピーしていた内容が消えてしまいます。ちょっとしたことですが、ユーザー体験が大きく向上します。

6.1. タイミング調整は必要

どの方式でも、キー送信後にちょっと待つ必要があります。

pyautogui.hotkey('command', 'v')
time.sleep(0.3)  # ここ大事
pyautogui.hotkey('command', 'return')
Code language: PHP (php)

0.3秒くらい待たないと、アプリがペーストを処理する前に送信してしまって、空のメッセージが送られたりします。

この待機時間は、アプリによって調整が必要かもしれません。

7. まとめ

macOSでテキストを自動送信する方法を4つ試してみました。

CGEvent方式が最強でした。送信中も自由に操作できて、権限も不要。ユーザー体験が圧倒的に良いです。

ただし、pyobjcが必要で、macOS専用です。

実用的には、CGEvent + pyautoguiのハイブリッドがベストだと思います。pyobjcがあれば最高の体験、なくても確実に動作します。

実際にツールを作っている方の参考になれば嬉しいです。