AutoHotkeyで外部からObsidianにメモを投げる

ツール活用
Fleeting Notes
Tired of manually transferring notes to Obsidian? Use Fleeting Notes to effortlessly capture your ideas and sync them wi...

先日、Obsidianの導入プラグイン一覧の記事を書き、追加更新分に「FleetingNotes」というスマホ同期プラグインについて触れました。
スマホのアプリを通じて、メモを自分のObsidianに直接投げ込めるものです。

Obsidian へ Fleeting Notes を紡ぐ|しょっさん
今年から Obsidian 使い続けていますけれども、思いつきでメモを残すにはちょっと使い勝手が悪いと感じていました。 しょっさんと Obsidian|しょっさん|note しょっさんが日々 Obsidian とともに暮らしている内容をまと...

Twitter経由のこの方の記事にて知ったわけですが、実際に使用したところ私のスイートスポットから微妙にズレた物でして。ボール半個分というか。
でもスマホからポンと走り書きを放り込める点はありがたいので、スマホのTwitterアイコンの横に陣取るぐらいのインパクトはありました。
また、ChromeとFirefoxからもメモを送り込める拡張機能もあります。

スマホからはFleetingNotesを利用するとして、メインのデスクトップPC上からはブラウザと言わず、Obsidian外であればどこからでもメモを投げ込める機能をAutoHotkeyで作れないかと試行錯誤してみました。

私の理想は、Obsidian上の「QuickAdd : CaptureMemo」にメモを投げ込みたい。
ぷーおんさんの記事に触れてから、私のObsidian上ではQuickAdd主体に機能化が進められてます。

[Obsidian] QuickAddプラグインを関西弁で解説するで!
兄ちゃん、兄ちゃん……それ、Obsidianか。 おーワシも知っとるがな。関西人ナメたらあかんで。 しかしな、兄ちゃん。今メモったその動作…… 遅いわ。 そんなもんメチャクチャ遅いがな。どのくらい遅いてそら、星野のスローカーブくらい遅いわ!

求められる機能

  • Obsidian以外のデスクトップ上で、ショートカットを発動できる。
  • 発動するとメモ入力欄をマウス付近に出現させる。
  • 内容は入力欄と送信ボタンのみ。Tabキーで交互に選択できる。
  • 送信するとObsidian上でQuickAddのCaptureMemoマクロを実行する。
  • Obsidian上では同ショートカットは発動しない(というかQuickAddと同じショートカットなので、そちらを発動させる)。
  • ブラウザ上でメモを取る場合は、閲覧しているWeb情報を付加する。
  • Obsidianが起動していない場合は、メモをmdファイルとしてObsidian所定ディレクトリに保存する。Obsidian起動時に読み込めるものとする。
  • メモを送信した後に、元のアプリをアクティブ状態にする。

可能ならObsidianが最小化されてる状態でも、そのままメモが送り込めれば最高だったのですが、調べた範囲では難しく、結果としてメモ投入時にObsidianをアクティブ化し「自動でショートカット実行→メモをペースト→OKボタン押す」という流れになりました。

外部からの操作はObsidianプラグイン「Advanced URI」も検討しましたが、QuickAdd開発者が示したサンプル内容でもObsidianがアクティブ状態のものでした。
またAutoHotkey側で非アクティブ状態でもコントロールできる方法も模索しましたが上手くいってません。

完成、か?

Chrome上で実行した様子
Obsidian上でメモが追加された様子

とりあえずできたもの公開

なんだかんだで、できたものを公開してみることにします。

私は自称「アミバ式プログラミング」と呼ぶ、いわゆる「当てずっぽ」「適当」「総当たり」な手法で、プログラムと呼ぶのもおこがましい代物をたまに自作しています。
従って以下のサンプルも見苦しい点あると思いますが、スルーしてください。

ちなみに AutoHotkey Ver.1 用です。

  • 2023/10/9 同じURL上でメモ連投の場合、2つ目以降のメモにはURL情報を付加しないよう変更
  • 2023/10/10 ESC処理の修正を行いました。
/*  ------------------------------------------------------------
; AHK Capture Memo for Obsidian  Ver.1.0.2
    - QuickAdd: Capture Memo「Alt+Q」をObsidian外で実現
    - マウス先に入力欄を表示。保存したらObsidian側に渡す
    - 入力欄表示時に再度「Alt+Q」「ESC」で窓消去
    - ブラウザの表示URLを自動的に補足する機能付き
    - Obsidian未起動時は所定フォルダにmdファイルを生成
    - 下記(1)(2)(3)(4)を個別設定
    - 2023/10/10 ESC処理の修正
*/
#Persistent
#SingleInstance, Force
#NoEnv
#HotkeyInterval, 2000
#MaxHotkeysPerInterval, 200
DetectHiddenWindows, On

SetTitleMatchMode,RegEx
CoordMode, Mouse, Screen
FileEncoding, UTF-8

; 設定ここから -------------------------------------------------

; (1) Obsidian未起動時に、mdファイル生成先 ※要設定
dirObsidian := "C:\<your-vault>\<your-favorite-folder>"

; (2) メモ欄のフォントサイズ、1行の文字数、メモ欄の行数目安
iFont := 14
iChar := 11
iFrmR := 5

; (3) AHKファイル直下にアイコンあれば読み込む
passIco := A_ScriptDir . "\ico\ico_ob_capturememo_256.ico"

; (4) 処理が早すぎて動かない場合はsleep時間を増やす(1000=1秒)
iSleep := 50

; 設定ここまで -------------------------------------------------

If ( !FileExist(dirObsidian) )
{
    Msgbox, Error: dirObsidian is not Exist!`n%dirObsidian%
    ExitApp
}

vAppname := "AHK Capture Memo for Obsidian"
Menu, Tray, Tip, %vAppname%
If ( FileExist(passIco) )
    Menu, Tray, Icon, %passIco%

Return

/* ------------------------------------------------------------
; ▼以下「AHK Capture Memo」アクティブ時
*/
#IfWinActive, AHK Capture Memo

; ESC:入力欄を終了
Esc::
	Gui, 99:Destroy
Exit

/* ------------------------------------------------------------
; ▼以下「Obsidian」非アクティブ時
; Alt+Q:メモ入力窓表示
*/
#IfWinNotActive, Obsidian v[\d\.]+$

!q::
    ; 初期化
    flgApp := 0
    vURL   :=
    vMemo  :=
    mTitle :=
    mID    :=
    mPID   :=

    ; すでに窓あれば終了して何もしない
    IfWinActive, %vAppname%
    {
        Gui, 99:Destroy
        Exit
    }

    MouseGetPos, mX, mY, mID
    WinGetTitle, mTitle, ahk_id %mID%
    WinGet, mPID, PID, ahk_id %mID%

    ClipSaved := ClipboardAll
    clipboard =

    ; - Microsoft​ Edge / ahk_class Chrome_WidgetWin_1
    ; - Google Chrome / ahk_class Chrome_WidgetWin_1
    ; — Mozilla Firefox / ahk_class MozillaWindowClass
    IfWinActive, Edge$
        flgApp := -23

    IfWinActive, Google Chrome$
        flgApp := -16

    IfWinActive, Mozilla Firefox$
        flgApp := -18

    If ( flgApp < 0 )
    {
        mTitle := SubStr(mTitle, 1, flgApp)
        Send, ^l ; Ctrl+L ブラウザ共通 URL欄選択
        sleep, %iSleep%
        Send, ^c
        ClipWait, 2
        If ( mTitle = "" )
            vURL := "(Web) [" . clipboard . "](" . clipboard . ")"
        Else
            vURL := "(Web) [" . mTitle . "](" . clipboard . ")"
    }

    iFrmW := Ceil( iFont * 1.6 * iChar )
    iFrmH := Ceil( iFont * 1.5 * iFrmR )
    iBtnY := iFrmH + 10
    iBtnH := Ceil( iFont * 1.5 ) + 4
    iGuiW := iFrmW + 10
    iGuiH := iFrmH + iBtnH + 15
    iGuiX := mX
    iGuiY := mY
    iRate := A_ScreenDPI / 96.0

    SysGet, iMcnt, MonitorCount
    Loop, %iMcnt%
    {
      SysGet, iM, Monitor, %A_Index%
      SysGet, iMwa, MonitorWorkArea, %A_Index%

        If ( mX - iMLeft >= 0 && mX - iMRight <= 0 && mY - iMTop >= 0 && mY - iMBottom <= 0 )
        {
            If ( mX + Ceil( iGuiW * iRate ) + 20 - iMwaRight >= 0 )
                iGuiX := iMwaRight - Ceil( iGuiW * iRate ) - 20

            If ( mY + Ceil( iGuiH * iRate ) + 100 - iMwaBottom >= 0 )
                iGuiY := iMwaBottom - Ceil( iGuiH * iRate ) - 100
        }
    }

    Gui, 99:Destroy
    Gui, 99:-MaximizeBox -MinimizeBox -Resize
    Gui, 99:Font, S%iFont% Norm c000000
    Gui, 99:Add, Edit, vvMemo x5 y5 w%iFrmW% h%iFrmH% , 
    Gui, 99:Add, Button, x5 y%iBtnY% w%iFrmW% h%iBtnH% Default gSubmitMemo , Capture Memo
    Gui, 99:Show, x%iGuiX% y%iGuiY% w%iGuiW% h%iGuiH%, %vAppname%

    Clipboard := ClipSaved
    ClipSaved = 
Return

/* ------------------------------------------------------------
; submitMemo メモ登録
*/
SubmitMemo:
    Gui, Submit

    ; ブラウザURLもなし&空欄の場合は処理なく終了
    If ( vMemo == "" && vURL == "" )
    {
        Gui, 99:Destroy
        Exit
    }

    ClipSaved := ClipboardAll
    clipboard =

    ; 前回分メモのURLと同じであればURL付加しない
    If ( vURL != vURLpre )
    {
        If ( vURL )
            clipboard := vURL

        If ( vURL && vMemo )
            clipboard .= "<br>"
    }

    If ( vMemo )
    {
        vMemo := RegExReplace(vMemo, "<", "\<")
        vMemo := RegExReplace(vMemo, ">", "\>")
        vMemo := RegExReplace(vMemo, "\n", "<br>")
        clipboard .= vMemo
        ClipWait, 2
    }

    IfWinExist, Obsidian v[\d\.]+$
    {
        WinActivate, - Obsidian v[\d\.]+$
        WinWaitActive, - Obsidian v[\d\.]+$
        sleep, %iSleep%
        Send, !q
        sleep, %iSleep%
        Send, ^v
        sleep, %iSleep%
        Send, {Tab}
        sleep, %iSleep%
        Send, {Enter}
        sleep, %iSleep%
    } 
    Else
    {
        vNote  :=

        If ( vMemo )
        {
            vNote  := vMemo
            vNote := RegExReplace(vNote, "<br>", "")
            vNote := RegExReplace(vNote, "\s", "")
            vNote := RegExReplace(vNote, " ", "")
            vNote := RegExReplace(vNote, "[\,\.\\/:;\*\?!<>\|\n\r]", "")
            vNote := Substr(vNote, 1, 20)
        }
        Else
            FormatTime, vNote, A_Now, yyyy-MM-dd_HH-mm-ss

        FormatTime, vStamp, A_Now, yyyy-MM-dd HH:mm

        ; Obsidian未起動時はmdファイルを生成
        FileAppend,
(
---
title: "%vNote%"
date created: "%vStamp%"
---
%clipboard%

), %dirObsidian%\%vNote%.md
        sleep, 1000
    }

    WinActivate, ahk_id %mID%

    vURLpre := vURL ; 前回分のURL情報を引き継ぎ

    flgApp := 0
    vURL   :=
    vMemo  :=
    mTitle :=
    vNote  :=

    Clipboard := ClipSaved
    ClipSaved = 

Return

; ScriptEnd

細かい補足

  • AHKスクリプト内の(1)~(4)の設定をまずご確認ください。
  • 私はQuickAdd: CaptureMemoをショートカット「Alt+Q」に設定しています。
  • このAHKについても同じショートカットです。
    これを変更したい場合は58行目と185行目辺りにある「!q」を変更してください。
  • Ctrlキーなら「^」、Shiftキー「+」、Altキー「!」、Winキー「#」です。
    「Ctrl+Shift+Q」なら「^+q」と記載します。
  • メモ欄で行う改行は、Obsidian側には「<br>」に変換されて改行されます。
  • メモ欄に含まれる「<」「>」はエスケープ処理されて、そのまま記録されます。
  • それ以外のエスケープ処理は行ってません。