エントリIDが指定されていれば上書き、されていなければ新規投稿となる。
writeHatenaEntryアクション単体では書き込めないので、Scriptable.appも使う。
Scriptable.appに保存するスクリプト「hatenaWrite」
// Drafts.appにコールバックする関数 function ReturnValueToDrafts(rO, x) { const rJ = JSON.stringify(rO) console.log(rJ) const rt = encodeURIComponent(rJ) const curl = `${params[x]}?result=${rt}` console.log(curl) Safari.open(curl) } // Draftsから渡されたパラメーターの読み込み const params = args.queryParameters console.log(params) const url = params.url const cdHatena = params.cdHatena const method = params.method const body = params.body // はてなからエントリーを読み込む const rq = new Request(url) rq.headers = {'Content-Type': 'application/atom+xml', 'Authorization': `Basic ${ cdHatena}`} rq.method = method rq.body = body let rXML rXML = await rq.loadString() const resp = rq.response console.log(resp) console.log(resp.statusCode) if (resp.statusCode == 200 || resp.statusCode == 201) { // 書き込みが成功した場合 console.log(rXML) // 受け取ったXMLから、エントリーのURLと、エントリーIDを含んだEdit用URLを抽出 const xmlParser = new XMLParser(rXML) let entry_url = edit_url= "" xmlParser.didStartElement = (elmntNameS, attrbO) => { if (elmntNameS == "link") { const attrbA = Object.entries(attrbO) if ( attrbA.some(a => a[0] == "rel" && a[1] == "alternate") ) { entry_url = attrbA.find(a => a[0] == "href")[1] console.log(entry_url) } if ( attrbA.some(a => a[0] == "rel" && a[1] == "edit") ) { edit_url = attrbA.find(a => a[0] == "href")[1] console.log(edit_url) } } } xmlParser.parse() // Draftsに書き込み成功時のデータを返す const rO = {} rO.statusCode = resp.statusCode rO.entry_url = entry_url rO.edit_url = edit_url ReturnValueToDrafts(rO, "x-success") } else { // Draftsに書き込み失敗時のデータを返す const rO = {} rO.statusCode = resp.statusCode rO.entry_url = rO.edit_url = "" ReturnValueToDrafts(rO, "x-error") } Script.complete()
注意点は、
- updatedの値が無い場合は、書き込んだ日時が自動的に入力される。
- 各項目、例えばentry_id:の後ろには、たとえ値が無くても半角スペースが必要。
- categoryは、値がある場合は、たとえ1個だけでも
[]
で囲む。 - draftが
yes
だと下書き、no
だと公開(公開範囲は公開範囲の設定による) - entry_idの値は、はてなブログから割り振られるものなので、手動で書き込まない。
- custom_urlは、
https://〜hate〜/entry/
よりも後ろの部分。値が無い場合は、はてなブログの設定 > 詳細設定 > フォーマット の記事URLの設定に従い割り振られる。
はてなブログAtomPub APIの仕様の制限で、
- 予約投稿ができない(はてなブログ API では予約投稿ができない - Neo's World)
- 一度「公開」にすると
draft: yes
にしても「下書き」に戻せない。(はてなブログのAtom Publishing Protocolについて - torum)
カスタムURLについては カスタムURLを指定した、はてなブログAtomPubによる記事投稿 - いっしきまさひこBLOG を参考にした。
Drafts.appのスクリプトアクションには、
The runtime is not a browser-based runtime, and does not contain DOM scripting capabilities.
と書かれている通り「XMLHttpRequest」や「Fetch API」が実装されていない様子。
その代わり、「HTTP」というオブジェクトを使うみたい。
しかし、そのHTTPオブジェクトのリクエストの、HTTPボディのエンコードフォーマットは「json」と「form」の2種類しかない。
AtomPubのボディはXMLで書かれた「string」。
Drafts.app単体では、ボディの必要の無い「GET」(読み込みや一覧読み込み)は可能だが、ボディが必要な「POST」(新規投稿)や「PUT」(既存のエントリの上書き)は不可能なようだった。
そこで、Scriptable.appをx-callback-urlで呼んで送信するようにした。
iOSやiPadOSだけでなく、ベータ版のScriptable for Macでも動いた。(MacBook Air(M1, 2020)にて)