Archive for 3月, 2016

2016/03/29 画面がDarkModeかLightModeかを判別

OS X 10.10以降でメニューバー、メニュー、Dockを暗くするDark Modeか通常モード(Light Mode)に設定されているかを判別するAppleScriptです。

状態の取得は手軽にできたものの、設定はまだAppleScriptだけではできていないため、とりあえずgithub上のdark-modeをコンパイルしてAppleScript Librariesの中に突っ込んでAppleScriptでラッピングして呼び出すようにして使っています。

lightmode.png
▲Light

darkmode.png
▲Dark

AppleScript名:画面がDarkModeかLightModeかを判別
– Created 2016-03-29 by Takaaki Naganoya
– 2016 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”

set curMode to (current application’s NSUserDefaults’s standardUserDefaults()’s stringForKey:“AppleInterfaceStyle”) as string
–>  ”Dark” or “Light”

★Click Here to Open This Script 

2016/03/23 AppleがMac Automation Scripting Guideを公開

3/21にAppleが「Mac Automation Scripting Guide」を公開しました。

macautomation.png

比較的初心者向けの内容となっています。以前からさんざん批判されていたような、「the」などの単語をわざわざ書いて英語に寄せた英語ライクな記法(Appleのエンジニアだけが書いている方言)をとっていないところは評価できますが、短く書けるところを長々と書いているあたりは見ていてゲッソリします。

そのほか、Appedixesの項目に「Objecitve-C to AppleScript(ASOC) Quick Translation Guide」もついています。

また、ほぼ同時に「Calendar Scripting Guide」も公開されています。

calendarscripting_resized.png

Calendar.appについてはReminder.appとの間でcalendarが識別できず、直す気もさらっさらないのかと個人的に思っていましたが、いま確認したら直っているようで。

2016/03/17 barCodeKitのじっけん

Oliver Drobnik氏によるオープンソースのフレームワーク「BarCodeKit」を利用してCode39のバーコード画像をデスクトップにPNG形式で出力するAppleScriptです。

あくまで実験レベルなので、オプション指定の内容に間違いが存在している可能性もあります。

BarCodeKitがサポートしているバーコード形式は、

–EAN-13/UPC-A
–EAN-8
–UPC-E
–Codabar
–Code 11
–Code 39 (plain, mod 43, Full ASCII plain and mod 43)
–Code 93 (with mod 47)
–Code 128
–Interleaved 2 of 5
–MSI - Modified Plessey (plain, mod 10, mod 11, mod 1010, mod 1011)
–Standard/Industrial 2 of 5
–Facing Identification Mark (FIM)
–EAN-2 and EAN-5 supplement codes
–ISBN (10 and 13 digits)
–ISMN
–ISSN
–POSTNET

とのことで、一番難易度の低いCode39でテストを行ってみたというところです(チェックサム・ディジットがないので簡単)。

出力した画像をKeynote書類に貼り付けてレーザープリンターで印刷し、バーコードリーダーでスキャンして指定したデータを取得できることを確認しました。

6423b354-4d0f-4307-9ef4-c26dddc7d822.png
▲本AppleScriptで出力したCode39画像

img_3342_resized.png

img_3343_resized.png

screens.png

OS X 10.10以降用にビルドしたフレームワークのバイナリを用意しておきました。興味のある方は自己責任で~/Library/Frameworksフォルダに入れてお試しください。

–> Download Framework Binary

AppleScript名:barCodeKitのじっけん
– Created 2016-03-17 by Takaaki Naganoya
– 2016 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “QuartzCore”
use framework “barCodeKit”

–Generate Barcode Image (Code 39)
set aBarcode to current application’s BCKCode39Code’s alloc()’s initWithContent:“PIYOMARU” |error|:(missing value)

set aRec to current application’s NSDictionary’s dictionaryWithObjectsAndKeys_(2.0, current application’s BCKCodeDrawingBarScaleOption, false, current application’s BCKCodeDrawingFillEmptyQuietZonesOption, true, current application’s BCKCodeDrawingPrintCaptionOption, false, current application’s BCKCodeDrawingMarkerBarsOverlapCaptionPercentOption, true, current application’s BCKCodeDrawingShowCheckDigitsOption, (current application’s NSColor’s whiteColor()), current application’s BCKCodeDrawingBackgroundColorOption, missing value)

set anNSImage to current application’s NSImage’s imageWithBarCode:aBarcode options:aRec

–Save Barcode Image as PNG
set aDesktopPath to (current application’s NSProcessInfo’s processInfo()’s environment()’s objectForKey:(“HOME”))’s stringByAppendingString:“/Desktop/”
set savePath to aDesktopPath’s stringByAppendingString:((current application’s NSUUID’s UUID()’s UUIDString())’s stringByAppendingString:“.png”)
saveNSImageAtPathAsPNG(anNSImage, savePath) of me

–NSImageを指定パスにPNG形式で保存
on saveNSImageAtPathAsPNG(anImage, outPath)
  set imageRep to anImage’s TIFFRepresentation()
  
set aRawimg to current application’s NSBitmapImageRep’s imageRepWithData:imageRep
  
set pathString to current application’s NSString’s stringWithString:outPath
  
set newPath to pathString’s stringByExpandingTildeInPath()
  
set myNewImageData to (aRawimg’s representationUsingType:(current application’s NSPNGFileType) |properties|:(missing value))
  
set aRes to (myNewImageData’s writeToFile:newPath atomically:true) as boolean
  
return aRes
end saveNSImageAtPathAsPNG

★Click Here to Open This Script 

2016/03/16 指定文字列からQRコード画像(PNG)をデスクトップに作成する

指定文字列からQRコードの画像をデスクトップに作成するAppleScriptです。

QRコードやバーコードを作成するためのFrameworkとしてはZXingObjCが有名で、これがAppleScriptでは扱えないCGImageを返してくるのでいろいろ悩んでいたのですが、OS X自体にQRコード作成機能があるとは予想外でした(悩んで損した、、、)。

58e2a470-ee99-4310-8a70-ba2b49c6bd98.png
▲本AppleScriptで生成したQRコードの画像

43d40cb9-6701-49e4-8cff-573570397e40.png
▲AppleScriptでバーコード画像も(CICode128BarcodeGenerator)生成できる(Code 128)

QRコードの認識機能自体もAppleScriptから呼び出せるので、本Scriptで生成したPNG画像を認識用のAppleScriptで確認して、元の文字列がデコードされることを確認しています(でも、なぜかCode128の1Dバーコードの認識機能が用意されていない。とても不思議)。

AppleScript名:指定文字列からQRコード画像(PNG)をデスクトップに作成する
– Created 2016-03-16 by Takaaki Naganoya
– 2016 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “QuartzCore”

set a to “mailto:maro@piyocast.com”

set aStr to current application’s NSString’s stringWithString:a
set strData to aStr’s dataUsingEncoding:(current application’s NSISOLatin1StringEncoding)
set qrFilter to current application’s CIFilter’s filterWithName:“CIQRCodeGenerator”
qrFilter’s setValue:strData forKey:“inputMessage”
qrFilter’s setValue:“H” forKey:“inputCorrectionLevel”
set anImage to qrFilter’s outputImage()

set aDesktopPath to (current application’s NSProcessInfo’s processInfo()’s environment()’s objectForKey:(“HOME”))’s stringByAppendingString:“/Desktop/”
set savePath to aDesktopPath’s stringByAppendingString:((current application’s NSUUID’s UUID()’s UUIDString())’s stringByAppendingString:“.png”)
saveNSImageAtPathAsPNG(anImage, savePath) of me

–NSImageを指定パスにPNG形式で保存
on saveNSImageAtPathAsPNG(anImage, outPath)
  set imageRep to anImage’s TIFFRepresentation()
  
set aRawimg to current application’s NSBitmapImageRep’s imageRepWithData:imageRep
  
set pathString to current application’s NSString’s stringWithString:outPath
  
set newPath to pathString’s stringByExpandingTildeInPath()
  
set myNewImageData to (aRawimg’s representationUsingType:(current application’s NSPNGFileType) |properties|:(missing value))
  
set aRes to (myNewImageData’s writeToFile:newPath atomically:true) as boolean
  
return aRes –成功ならtrue、失敗ならfalseが返る
end saveNSImageAtPathAsPNG

★Click Here to Open This Script 

2016/03/13 AppleScriptからWebアプリを操作する場合に用いるWebブラウザは?

Mac系の会合で質問がありました。

「AppleScriptから、Webブラウザに表示されているボタンなどを操作してさまざまなWebアプリを操作できることはわかったが、その際にWebブラウザは何を使うべきなのか?」

script_webbrowser.png

とりあえずの最低条件は、「AppleScript用語辞書を備えているWebブラウザ」ということです。WindowやDocumentをopenしたりcloseでき、その中のTabなどにアクセスでき、URLを設定したり取得したり、ページ内のソースを取得できたりというひととおりの操作ができることが条件です。

sleipnir.png
Sleipnir 4.5.1にはAppleScript用語辞書が入っているものの、実装を間違っているらしくScript Editorでオープンすると真っ白な用語辞書が表示される(直接飲み会でF社の方に報告済み、、、なんですけど、、、、)

FireFoxにもAppleScript用語辞書が入ってはいるものの、肝心の「do javascript」命令がないので、「これで何をしろと??」という状態です。Vivaldiも同様に「do javascript」命令がないので、こうした用途には使えません。

次に、「GUI Scriptingで操作するのに問題がないこと」という条件がつきます。 Webコンテンツを操作するには、JavaScript経由でアクセスしたり、GUI Scriptingでアクセスしたりします。これで脱落するものはありません。だいたいは大丈夫です。

そして、「Webブラウザ独自のアプリストアなどの仕組みによって、予期しないダイアログ表示などが行われないこと」「アップデート通知でいきなり終了したりしないこと」というあたりで、Google Chromeが怪しくなってきます。

結局、こうした用途にOS X標準装備のSafari以外のWebブラウザを使うこと自体がリスキーなので、Safariを使ってくださいという話になります。User AgentでSafari以外のものを使うことがリスキー(銀行のサイトへのアクセスなどで不安が、、、)なので、かさねがさねSafari以外のものを使うことが考えられません。

最近では、Mac miniなどをAppleScript実行専用のロボットとして設定すると、まっさらな状態のOS X環境では、知らないうちに勝手にiTunesを起動されて困ります。iTunes Helperなどのアプリケーションのプロセスはみられませんが、勘弁してほしいところです。

2016/03/13 Google Translate APIでサポート言語一覧を指定言語で取得する

Google Translate API(REST API)を呼び出して、翻訳可能な言語の名称一覧を指定言語(Japanese)で取得するAppleScriptです。

実行するには、Shane StanleyのBridgePlus Script Libraryを実行するMacの~/Library/Script Librariesにインストールし、GoogleのAPI利用登録を行ってAPI Keyを取得しておく必要があります。ご自分のAPI Keyを下記リストのmyAPIKeyに代入(リスト中では伏字)してから実行してください。

実行結果から、Google Translate APIが104の言語をサポートしていることがわかります。

この程度の問い合わせなら問題なく実行できているのですが、いざ翻訳させようとするとまだクリアーできていない困難が(汗) Googleのサービス側のログとかを見られると、どこで間違っているのか分かりそうですが、、、、というか、そういうサービス自体が存在していそうな、、、、

AppleScript名:GET method REST API_Google Translate API サポート言語一覧を取得
– Created 2016-03-03 by Takaaki Naganoya
– 2016 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use BridgePlus : script “BridgePlus” –https://www.macosxautomation.com/applescript/apps/BridgePlus.html

set myAPIKey to “XXxxXxXXXxXxX-XxXXxXXXxxxxXXXXxXxXXxXXX”
set reqURLStr to “https://www.googleapis.com/language/translate/v2/languages?key=” & myAPIKey & “&target=ja”

set aRes to callRestGETAPIAndParseResults(reqURLStr) of me

set aRESTres to json of aRes
–>  (NSDictionary) {error:{message:”Bad Request”, errors:{{reason:”keyInvalid”, message:”Bad Request”, domain:”usageLimits”}}, code:400}}–エラー時

–>  (NSDictionary) {data:{languages:{{name:”アイスランド語”, language:”is”}…… {name:”英語”, language:”en”}, {name:”韓国語”, language:”ko”}, {name:”中国語(簡体)”, language:”zh”}, {name:”中国語(繁体)”, language:”zh-TW”}, {name:”日本語”, language:”ja”}}}}

–return (aRESTres’s valueForKeyPath:”data.languages.name”)’s |count|()
–>  104

set aRESCode to responseCode of aRes
–>  200

set aRESHeader to responseHeader of aRes
–>  {Content-Type:”application/json; charset=UTF-8″, alt-svc:”quic=\”:443\”; ma=2592000; v=\”31,30,29,28,27,26,25\”", alternate-protocol:”443:quic,p=1″, Content-Encoding:”gzip”, Server:”GSE”, x-xss-protection:”1; mode=block”, Expires:”Sun, 13 Mar 2016 00:45:39 GMT”, Cache-Control:”private, max-age=0″, Date:”Sun, 13 Mar 2016 00:45:39 GMT”, Content-Length:”132″, x-content-type-options:”nosniff”, x-frame-options:”SAMEORIGIN”, Vary:”Origin, X-Origin”}

–GET methodのREST APIを呼ぶ
on callRestGETAPIAndParseResults(aURL)
  load framework
  
set aRequest to current application’s NSMutableURLRequest’s requestWithURL:(current application’s |NSURL|’s URLWithString:aURL)
  
aRequest’s setHTTPMethod:“GET”
  
  
set aRes to current application’s NSURLConnection’s sendSynchronousRequest:aRequest returningResponse:(reference) |error|:(missing value)
  
set resList to aRes as list
  
  
set bRes to contents of (first item of resList)
  
set resStr to current application’s NSString’s alloc()’s initWithData:bRes encoding:(current application’s NSUTF8StringEncoding)
  
  
set jsonString to current application’s NSString’s stringWithString:resStr
  
set jsonData to jsonString’s dataUsingEncoding:(current application’s NSUTF8StringEncoding)
  
set aJsonDict to current application’s NSJSONSerialization’s JSONObjectWithData:jsonData options:0 |error|:(missing value)
  
  
–Get Response Code
  
set dRes to contents of second item of resList
  
set resCode to ASify from (dRes’s statusCode())
  
  
–Get Response Header
  
set resHeaders to ASify from (dRes’s allHeaderFields())
  
  
return {json:aJsonDict, responseCode:resCode, responseHeader:resHeaders}
  
end callRestGETAPIAndParseResults

★Click Here to Open This Script 

2016/03/11 shellの出力結果をスペースでparseする

shellの出力結果を利用する際に、スペースでparseするAppleScriptです。

連続するスペースキャラクターでparseしつつ不要なゴミを削除して必要な項目だけ取得するというものです。

AppleScript名:shellの出力結果をスペースでparseする
– Created 2016-03-11 by Takaaki Naganoya
– 2016 Piyomaru Software
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"

set a to "3 0×4248387 1920×1200 0 0 -1920 -1200 0 [main]"

set aStr to (current application’s NSString’s stringWithString:a)
–> (NSString) "3 0×4248387 1920×1200 0 0 -1920 -1200 0 [main]"

set aLine to (aStr’s componentsSeparatedByString:" ")
–> (NSArray) {"3", "", "0×4248387", "", "", "", "", "1920×1200", "", "", "", "", "", "0", "", "", "", "", "0", "", "-1920", "", "-1200", "", "", "", "", "", "0", "", "", "", "[main]"}

(aLine’s removeObject:"")
–> (NSArray) {"3", "0×4248387", "1920×1200", "0", "0", "-1920", "-1200", "0", "[main]"}

set bList to aLine as list
–>  {"3", "0×4248387", "1920×1200", "0", "0", "-1920", "-1200", "0", "[main]"}

★Click Here to Open This Script 

2016/03/07 与えられたNSDataに指定文字列をUTF-8のencodingでNSData化しつつ末尾に連結(結果は参照渡し)

与えられたNSDataに対して、文字列データをUTF-8で評価してNSDataにして追加するAppleScriptです。

NSDataへの追加について、appendData: usingEncoding: というメソッドがOS Xに存在していないようなので(iOSにあるのに)、サブルーチンで作っておきました。

巨大なデータ同士の追加になった場合への配慮として、サブルーチンを参照渡し(call by reference)で呼び出しています。

巨大なAppleScriptのプログラムを組む際には、一定以上に複雑にならないようになるべく値渡しのサブルーチンを用意し、単純な世界観で統一していますが、こういうケースは参照渡しだろうと。

AppleScript名:与えられたNSDataに指定文字列をUTF-8のencodingでNSData化しつつ末尾に連結(結果は参照渡し)
– Created 2016-03-07 by Takaaki Naganoya
– 2016 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”

set postBody to current application’s NSMutableData’s |data|() –empty data

appendDataWithString(postBody, “123″)
postBody
–>  (NSData) <313233>

appendDataWithString(postBody, “456″)
postBody
–>  (NSData) <31323334 3536>

–与えられたNSDataに指定文字列をUTF-8のencodingでNSData化しつつ末尾に連結(結果は参照渡し)
on appendDataWithString(aData, aString)
  set aStr to current application’s NSString’s stringWithString:aString
  
set aBin to aStr’s dataUsingEncoding:(current application’s NSUTF8StringEncoding)
  
aData’s appendData:aBin
end appendDataWithString

★Click Here to Open This Script 

2016/03/06 REST APIに対してGET、POST、PUT、DELETEのmethodを呼び出す

AppleScriptでWeb上のREST APIを呼び出すじっけんです。Web上にあるREST APIの実験用サービスJSONPlaceholderをAppleScriptから呼び出して各種メソッド呼び出しの実証を行ってみました。

できてしまえばたいしたことはありませんが、地道に調査しておかないと今日明日でいきなりやれと言われても困ってしまいます。

実際に、NTTdocomoの形態素解析のREST APIをmethod=POSTでAppleScriptから呼び出してみたところ、

–> (NSDictionary) {request_id:”record001″, word_list:{{{”私”}, {”の”}, {”名前”}, {”は”}, {” ”}, {”長野”}, {”谷”}, {”です”}, {”。”}}}, info_filter:”form”}

となりました(words ofの実行結果と変わり映えしない、、、、)。固有名詞としてあらかじめ人名を登録しておきたいケースがほとんどなので、ちょっとこれだと使えない感じではあります。ただ、実際に稼働しているAPIを呼べることは実証できました。

AppleScript名:GET method REST API
– Created 2016-03-03 by Takaaki Naganoya
– 2016 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use BridgePlus : script “BridgePlus”

set reqURLStr to “http://jsonplaceholder.typicode.com/posts”

set aRes to callRestGETAPIAndParseResults(reqURLStr) of me

set aRESTres to json of aRes
–>  (NSArray) {{body:”quia et suscipit suscipit recusandae consequuntur expedita et cum reprehenderit molestiae ut ut quas totam nostrum rerum est autem sunt rem eveniet architecto”, id:1, title:”sunt aut facere repellat provident occaecati excepturi optio reprehenderit”, userId:1},…

set aRESCode to responseCode of aRes
–>  200

set aRESHeader to responseHeader of aRes
–>  {Content-Type:”application/json; charset=utf-8″, Pragma:”no-cache”, X-Powered-By:”Express”, Via:”1.1 vegur”, Server:”Cowboy”, Expires:”-1″, Cache-Control:”no-cache”, Date:”Sun, 06 Mar 2016 07:27:26 GMT”, Access-Control-Allow-Credentials:”true”, Content-Length:”27520″, Connection:”keep-alive”, X-Content-Type-Options:”nosniff”, Etag:”W/\”6b80-uPwhAkDat3Fl5TugzmyYpQ\”", Vary:”Origin”}

–GET methodのREST APIを呼ぶ
on callRestGETAPIAndParseResults(aURL)
  load framework
  
set aRequest to current application’s NSMutableURLRequest’s requestWithURL:(current application’s |NSURL|’s URLWithString:aURL)
  
aRequest’s setHTTPMethod:“GET”
  
  
set aRes to current application’s NSURLConnection’s sendSynchronousRequest:aRequest returningResponse:(reference) |error|:(missing value)
  
set resList to aRes as list
  
  
set bRes to contents of (first item of resList)
  
set resStr to current application’s NSString’s alloc()’s initWithData:bRes encoding:(current application’s NSUTF8StringEncoding)
  
  
set jsonString to current application’s NSString’s stringWithString:resStr
  
set jsonData to jsonString’s dataUsingEncoding:(current application’s NSUTF8StringEncoding)
  
set aJsonDict to current application’s NSJSONSerialization’s JSONObjectWithData:jsonData options:0 |error|:(missing value)
  
  
–Get Response Code
  
set dRes to contents of second item of resList
  
set resCode to ASify from (dRes’s statusCode())
  
  
–Get Response Header
  
set resHeaders to ASify from (dRes’s allHeaderFields())
  
  
return {json:aJsonDict, responseCode:resCode, responseHeader:resHeaders}
  
end callRestGETAPIAndParseResults

★Click Here to Open This Script 

AppleScript名:POST method REST API
– Created 2016-03-06 by Takaaki Naganoya
– 2016 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use BridgePlus : script “BridgePlus”

set reqURLStr to “http://jsonplaceholder.typicode.com/posts”
set aPostData to {title:“foo”, body:“bar”, userId:1}

set aRes to callRestPOSTAPIAndParseResults(reqURLStr, aPostData) of me

set aRESTres to json of aRes
–>  (NSDictionary) {body:”bar”, id:101, title:”foo”, userId:1}

set aRESCode to responseCode of aRes
–>  200

set aRESHeader to responseHeader of aRes
–>  {Content-Type:”application/json; charset=utf-8″, Pragma:”no-cache”, X-Powered-By:”Express”, Via:”1.1 vegur”, Server:”Cowboy”, Expires:”-1″, Cache-Control:”no-cache”, Date:”Sun, 06 Mar 2016 07:47:56 GMT”, Access-Control-Allow-Credentials:”true”, Content-Length:”65″, Connection:”keep-alive”, X-Content-Type-Options:”nosniff”, Etag:”W/\”41-DL1IzEbjanDFwm6hF2BfJw\”", Vary:”Origin, X-HTTP-Method-Override”}

–POST methodのREST APIを呼ぶ
on callRestPOSTAPIAndParseResults(aURL, aPostData)
  load framework
  
set dataJson to current application’s NSJSONSerialization’s dataWithJSONObject:aPostData options:0 |error|:(missing value)
  
set aRequest to current application’s NSMutableURLRequest’s requestWithURL:(current application’s |NSURL|’s URLWithString:aURL)
  
  
aRequest’s setHTTPMethod:“POST”
  
aRequest’s setHTTPBody:dataJson
  
aRequest’s setValue:“application/json” forHTTPHeaderField:“Accept”
  
aRequest’s setValue:“application/json” forHTTPHeaderField:“Content-Type”
  
  
set aRes to current application’s NSURLConnection’s sendSynchronousRequest:aRequest returningResponse:(reference) |error|:(missing value)
  
set resList to aRes as list
  
  
set bRes to contents of (first item of resList)
  
set resStr to current application’s NSString’s alloc()’s initWithData:bRes encoding:(current application’s NSUTF8StringEncoding)
  
  
set jsonString to current application’s NSString’s stringWithString:resStr
  
set jsonData to jsonString’s dataUsingEncoding:(current application’s NSUTF8StringEncoding)
  
set aJsonDict to current application’s NSJSONSerialization’s JSONObjectWithData:jsonData options:0 |error|:(missing value)
  
  
–Get Response Code
  
set dRes to contents of second item of resList
  
set resCode to ASify from (dRes’s statusCode())
  
  
–Get Response Header
  
set resHeaders to ASify from (dRes’s allHeaderFields())
  
  
return {json:aJsonDict, responseCode:resCode, responseHeader:resHeaders}
  
end callRestPOSTAPIAndParseResults

★Click Here to Open This Script 

AppleScript名:PUT method REST API
– Created 2016-03-06 by Takaaki Naganoya
– 2016 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use BridgePlus : script “BridgePlus”

set reqURLStr to “http://jsonplaceholder.typicode.com/posts/1″
set aPostData to {title:“ぴよまるソフトウェア”, body:“PUTのじっけん”, userId:1}
–set aPostData to {title:”foo”, body:”bar”, userId:1}

set aRes to callRestPUTAPIAndParseResults(reqURLStr, aPostData) of me

set aRESTres to json of aRes
–>  (NSDictionary) {body:”PUTのじっけん”, id:1, title:”ぴよまるソフトウェア”, userId:1}
return aRESTres
set aRESCode to responseCode of aRes
–>  200

set aRESHeader to responseHeader of aRes
–>  {Content-Type:”application/json; charset=utf-8″, Pragma:”no-cache”, X-Powered-By:”Express”, Via:”1.1 vegur”, Server:”Cowboy”, Expires:”-1″, Cache-Control:”no-cache”, Date:”Sun, 06 Mar 2016 07:47:56 GMT”, Access-Control-Allow-Credentials:”true”, Content-Length:”65″, Connection:”keep-alive”, X-Content-Type-Options:”nosniff”, Etag:”W/\”41-DL1IzEbjanDFwm6hF2BfJw\”", Vary:”Origin, X-HTTP-Method-Override”}

–PUT methodのREST APIを呼ぶ
on callRestPUTAPIAndParseResults(aURL, aPostData)
  load framework
  
set dataJson to current application’s NSJSONSerialization’s dataWithJSONObject:aPostData options:0 |error|:(missing value)
  
set aRequest to current application’s NSMutableURLRequest’s requestWithURL:(current application’s |NSURL|’s URLWithString:aURL)
  
  
aRequest’s setHTTPMethod:“PUT”
  
aRequest’s setHTTPBody:dataJson
  
aRequest’s setValue:“application/json” forHTTPHeaderField:“Accept”
  
aRequest’s setValue:“application/json” forHTTPHeaderField:“Content-Type”
  
  
set aRes to current application’s NSURLConnection’s sendSynchronousRequest:aRequest returningResponse:(reference) |error|:(missing value)
  
set resList to aRes as list
  
  
set bRes to contents of (first item of resList)
  
set resStr to current application’s NSString’s alloc()’s initWithData:bRes encoding:(current application’s NSUTF8StringEncoding)
  
  
set jsonString to current application’s NSString’s stringWithString:resStr
  
set jsonData to jsonString’s dataUsingEncoding:(current application’s NSUTF8StringEncoding)
  
set aJsonDict to current application’s NSJSONSerialization’s JSONObjectWithData:jsonData options:0 |error|:(missing value)
  
  
–Get Response Code
  
set dRes to contents of second item of resList
  
set resCode to ASify from (dRes’s statusCode())
  
  
–Get Response Header
  
set resHeaders to ASify from (dRes’s allHeaderFields())
  
  
return {json:aJsonDict, responseCode:resCode, responseHeader:resHeaders}
  
end callRestPUTAPIAndParseResults

★Click Here to Open This Script 

AppleScript名:DELETE method REST API
– Created 2016-03-06 by Takaaki Naganoya
– 2016 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use BridgePlus : script “BridgePlus”

set reqURLStr to “http://jsonplaceholder.typicode.com/posts/1″

set aRes to callRestDELETEAPIAndParseResults(reqURLStr) of me

set aRESTres to json of aRes
–>  (NSDictionary) {}

set aRESCode to responseCode of aRes
–>  200

set aRESHeader to responseHeader of aRes
–>  {Content-Type:”application/json; charset=utf-8″, Pragma:”no-cache”, X-Powered-By:”Express”, Via:”1.1 vegur”, Server:”Cowboy”, Expires:”-1″, Cache-Control:”no-cache”, Date:”Sun, 06 Mar 2016 07:53:51 GMT”, Access-Control-Allow-Credentials:”true”, Content-Length:”2″, Connection:”keep-alive”, X-Content-Type-Options:”nosniff”, Etag:”W/\”2-mZFLkyvTelC5g8XnyQrpOw\”", Vary:”Origin”}

–DELETE methodのREST APIを呼ぶ
on callRestDELETEAPIAndParseResults(aURL)
  load framework
  
set aRequest to current application’s NSMutableURLRequest’s requestWithURL:(current application’s |NSURL|’s URLWithString:aURL)
  
set respF to false
  
  
aRequest’s setHTTPMethod:“DELETE”
  
  
set aRes to current application’s NSURLConnection’s sendSynchronousRequest:aRequest returningResponse:(reference) |error|:(missing value)
  
set resList to aRes as list
  
  
set bRes to contents of (first item of resList)
  
set resStr to current application’s NSString’s alloc()’s initWithData:bRes encoding:(current application’s NSUTF8StringEncoding)
  
  
set jsonString to current application’s NSString’s stringWithString:resStr
  
set jsonData to jsonString’s dataUsingEncoding:(current application’s NSUTF8StringEncoding)
  
set aJsonDict to current application’s NSJSONSerialization’s JSONObjectWithData:jsonData options:0 |error|:(missing value)
  
  
–Get Response Code
  
set dRes to contents of second item of resList
  
set resCode to ASify from (dRes’s statusCode())
  
  
–Get Response Header
  
set resHeaders to ASify from (dRes’s allHeaderFields())
  
  
return {json:aJsonDict, responseCode:resCode, responseHeader:resHeaders}
  
end callRestDELETEAPIAndParseResults

★Click Here to Open This Script 

2016/03/06 REST APIを呼ぶじっけん v2

Web上のREST APIのサービスを呼び出すAppleScriptの改良版です。

v1ではただREST APIを呼んで、結果をNSStringで取得するだけでしたが、各種仕様を確認してJSON文字列とみなしNSJSONSerializationでparseしてNSDictionaryなりNSDictionary in NSArrayにして返します。

REST APIの返り値についてはXML/JSONのどちらを用いるかとくに規約はないようですが、一般的にはJSONとのことなのでJSONとみなしてparse。また、parseした結果をAppleScriptのオブジェクトにいったん変換してしまうと、構造上データを取り出せなくなってしまうケースがあるため、Cocoaオブジェクトの状態のままで返すようにしてみました(json of aRes)。

本ScriptはMethod=GETで呼んでいる超簡単なAPIの呼び出し例ですが、その他のケース(POST、PUT、DELETE、HEAD、OPTIONS、PATCH、COPY、SEARCH)についても呼び出し方を調べておきたいところです。

AppleScript名:REST APIを呼ぶじっけん v2
– Created 2016-03-03 by Takaaki Naganoya
– 2016 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use BridgePlus : script “BridgePlus”

set reqURLStr to “http://demos.xojo.com/EEWS/index.cgi/GetAllCustomers”
–set reqURLStr to “https://api.github.com/repos/mmattozzi/cocoa-rest-client/releases”

set aRes to callRestAPIAndParseResults(reqURLStr) of me

set aRESTres to json of aRes –ASのRecordに変換してしまうと、あとで値が取り出せなくなるケースがあったので、NSDictionaryのまま返している
–>  (NSDictionary) {GetAllCustomers:{10044:{Zip:”63101″, City:”St. Louis”, FirstName:”Beatrice”, LastName:”Fulton”, State:”MO”}, …

set aRESCode to responseCode of aRes
–>  404

set aRESHeader to responseHeader of aRes
–>  {Content-Encoding:”gzip”, Content-Type:”text/html; charset=UTF-8″, Content-Length:”5376″, Connection:”close”, Server:”Apache/2.2.15 (CentOS)”, Date:”Sat, 05 Mar 2016 15:37:26 GMT”}

on callRestAPIAndParseResults(aURL)
  
  
load framework
  
  
set aRequest to current application’s NSURLRequest’s requestWithURL:(current application’s |NSURL|’s URLWithString:aURL)
  
set respF to false
  
set aRes to current application’s NSURLConnection’s sendSynchronousRequest:aRequest returningResponse:(reference) |error|:(missing value)
  
set resList to aRes as list
  
–log {length of resList}
  
–> {2}
  
  
set bRes to contents of (first item of resList)
  
set resStr to current application’s NSString’s alloc()’s initWithData:bRes encoding:(current application’s NSUTF8StringEncoding)
  
–>  (NSString) “{”GetAllCustomers”:{”10174″:{”FirstName”:”Abdul”,”LastName”:”Mcconnell”,”City”:”Colorado Springs”,”State”:”CO”,”Zip”:”80935″},….
  
  
set jsonString to current application’s NSString’s stringWithString:resStr
  
set jsonData to jsonString’s dataUsingEncoding:(current application’s NSUTF8StringEncoding)
  
set aJsonDict to current application’s NSJSONSerialization’s JSONObjectWithData:jsonData options:0 |error|:(missing value)
  
–set cRes to ASify from aJsonDict –json
  
  
–Get Response Code
  
set dRes to contents of second item of resList
  
set resCode to ASify from (dRes’s statusCode())
  
–>  404
  
  
–Get Response Header
  
set resHeaders to ASify from (dRes’s allHeaderFields())
  
–>  (NSDictionary) {Content-Encoding:”gzip”, Content-Type:”text/html; charset=UTF-8″, Content-Length:”5376″, Connection:”close”, Server:”Apache/2.2.15 (CentOS)”, Date:”Thu, 03 Mar 2016 13:44:22 GMT”}
  
  
return {json:aJsonDict, responseCode:resCode, responseHeader:resHeaders}
  
end callRestAPIAndParseResults

★Click Here to Open This Script 

2016/03/03 REST APIを呼ぶじっけん

Web上のREST APIのサービスを呼び出すAppleScriptです。

なかなか呼び出すのに苦労させられていたWeb上のREST APIを呼び出すための実験的なAppleScriptのコードです。

いろいろなAPIを見て回っていると、音声認識APIなどは音声ファイルをアップロードしてやる必要があるケースもあるため、こんな簡単な呼び出し方だけでは済まないようですが、、、

rest1.png

とりあえず、REST APIのテスト用アプリケーション「Cocoa Rest Client」で呼べることを確認したAPIについて、AppleScriptの実行結果と付け合わせて納得できる内容が得られたので、満足しています。

rest2.png

ほかにも、githubのREST APIを呼んでみたところ、きちんと結果が取得できました。

AppleScript名:REST APIを呼ぶじっけん
– Created 2016-03-03 by Takaaki Naganoya
– 2016 Piyomaru Software
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"

set reqURLStr to "http://demos.xojo.com/EEWS/index.cgi/GetAllCustomers"
–set reqURLStr to "https://api.github.com/repos/mmattozzi/cocoa-rest-client/releases"

set aRequest to current application’s NSURLRequest’s requestWithURL:(current application’s |NSURL|’s URLWithString:reqURLStr)
set respF to false
set aRes to current application’s NSURLConnection’s sendSynchronousRequest:aRequest returningResponse:(reference) |error|:(missing value)
set resList to aRes as list
log {length of resList}
–> {2}

set bRes to contents of (first item of resList)
set resStr to current application’s NSString’s alloc()’s initWithData:bRes encoding:(current application’s NSUTF8StringEncoding)
–>  (NSString) "{"GetAllCustomers":{"10174":{"FirstName":"Abdul","LastName":"Mcconnell","City":"Colorado Springs","State":"CO","Zip":"80935"},….

–Get Response Code
set cRes to contents of second item of resList
set resCode to cRes’s statusCode()
–>  404

–Get Response Header
set resHeaders to cRes’s allHeaderFields()
–>  (NSDictionary) {Content-Encoding:"gzip", Content-Type:"text/html; charset=UTF-8", Content-Length:"5376", Connection:"close", Server:"Apache/2.2.15 (CentOS)", Date:"Thu, 03 Mar 2016 13:44:22 GMT"}

★Click Here to Open This Script 

2016/03/03 SSKeychainのじっけん

Sam Soffes氏によるオープンソースのKeychainアクセス用Framework「SSKeychain」を用いてKeychainにアクセスするAppleScriptです。

もともとOS X 10.6までは「Keychain Scripting」というAppleScriptから利用する専用のGUIなしアプリケーションがOSに標準装備されていたのですが、OS X 10.7で廃止されたため、shellのsecurityコマンドを利用するなどの代替手段が模索されてきました。

OS X 10.10以降は通常のAppleScriptでもCocoaの機能が利用できるようになったため、このようなフレームワーク経由でKeychainにアクセス(書き込み、読み出し、存在確認、削除)できるようになりました。

keychain.png

OS X 10.10以降用にビルドしたFrameworkのバイナリを用意したため、各自自己責任でFrameworkを~/Library/Frameworksフォルダに入れておためしください。

–> Download Framework Binary

AppleScript名:SSKeychainのじっけん
– Created 2016-03-02 by Takaaki Naganoya
– 2016 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “SSKeychain” –https://github.com/soffes/sskeychain

set kSSKeychainServiceName to “SSToolkitTestService”
set kSSKeychainAccountName to “SSToolkitTestAccount”
set kSSKeychainPassword to “SSToolkitTestPassword”
set kSSKeychainLabel to “SSToolkitLabel”

– New item
set aQuery to current application’s SSKeychainQuery’s alloc()’s init()
aQuery’s setPassword:kSSKeychainPassword
aQuery’s setService:kSSKeychainServiceName
aQuery’s setAccount:kSSKeychainAccountName
aQuery’s setLabel:kSSKeychainLabel
set aRes to (aQuery’s |save|:(missing value)) as boolean

– Look up
set bQuery to current application’s SSKeychainQuery’s alloc()’s init()
bQuery’s setService:kSSKeychainServiceName
bQuery’s setAccount:kSSKeychainAccountName
bQuery’s setPassword:(missing value)
set bRes to (bQuery’s fetch:(missing value)) as boolean
log {bQuery’s |password|() as string, kSSKeychainPassword}

– Search for all accounts
set cQuery to current application’s SSKeychainQuery’s alloc()’s init()
set accList to cQuery’s fetchAll:(missing value)
–>  (NSArray) {{mdat:(NSDate) 2010-02-23 08:36:19 +0000, cdat:(NSDate) 2010-02-23 08:36:19 +0000, acct:”GoogleJapaneseInput”, class:”genp”, svce:”GoogleJapaneseInput”, labl:”GoogleJapaneseInput”, crtr:9999999999}, ……

– Check accounts for service
cQuery’s setService:kSSKeychainServiceName
set aRes to cQuery’s fetchAll:(missing value)
–>  (NSArray) {{mdat:(NSDate) 2016-03-02 13:54:54 +0000, cdat:(NSDate) 2016-03-02 13:44:19 +0000, svce:”SSToolkitTestService”, acct:”SSToolkitTestAccount”, class:”genp”, labl:”SSToolkitLabel”}}

– Delete
set dQuery to current application’s SSKeychainQuery’s alloc()’s init()
dQuery’s setService:kSSKeychainServiceName
dQuery’s setAccount:kSSKeychainAccountName
set dRes to (dQuery’s deleteItem:(missing value)) as boolean
–>  true

–Test Password Object
set eQuery to current application’s SSKeychainQuery’s alloc()’s init()
eQuery’s setService:kSSKeychainServiceName
eQuery’s setAccount:kSSKeychainAccountName
set eDic1 to current application’s NSDictionary’s dictionaryWithObjectsAndKeys_(42, “number”, “Hello World”, “string”, missing value)
eQuery’s setPasswordObject:eDic1
set fRes to (eQuery’s |save|:(missing value)) as boolean

set fQuery to current application’s SSKeychainQuery’s alloc()’s init()
fQuery’s setService:kSSKeychainServiceName
fQuery’s setAccount:kSSKeychainAccountName
fQuery’s setPassword:(missing value)
fQuery’s fetch:(missing value)
log {fQuery’s passwordObject(), eDic1}

★Click Here to Open This Script 

2016/03/01 ftpKitのじっけん ftpサーバーからファイル一覧を取得

Nico Kreipke氏によるオープンソースの「FTPManager」をFramework化した「ftpkit.framework」を呼び出して、指定ftpサーバーのファイル名一覧をlistで取得するAppleScriptです。

GUIベースのアプリケーションでは、「Transmit」という定番・信頼のソフトウェアが存在していますが、GUIベースのアプリケーションでは並列処理時には使えません。そのため、このようなフレームワーク化したプログラムが必要になってくるわけです。

とりあえず、FTPManagerを用いてファイルのアップロード/ダウンロード/chmod/削除/新規フォルダ作成などをひととおりAppleScriptから実行できることを確認しました。

FTPManager以外にもFTP機能を提供するプログラムは多々あるので、いろいろためしてみたいところです。とくに、sftpでアクセスできるものがあるとよさそうです(FTPはちょっと、、、NMSSHがビルドできればsftpが使えるのに、、)。

とりあえず、OS X 10.10以降で動作するようにFTPManagerをFramework化してビルドしたバイナリを用意しておきました。自己責任でframeworkを~/Library/Frameworksフォルダ以下にアーカイブを展開してコピーしたうえで本Scriptをおためしください。

–> Download Framework Binary

AppleScript名:ftpKitのじっけん ファイル一覧を取得
– Created 2016-03-01by Takaaki Naganoya
– 2016 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “ftpKit” –https://github.com/nkreipke/FTPManager

set serverURL to “ftp://xxx.xxxxxxxx.xxx/xxxxxxxx.xxx”
set serverUser to “xxxxxxxx@xxxxxxxx.xxx”
set serverPassword to text returned of (display dialog “Input FTP Password” default answer “” with hidden answer)

set aFtpManager to current application’s FTPManager’s alloc()’s init()
set successF to false

set srv to current application’s FMServer’s serverWithDestination:serverURL username:serverUser |password|:serverPassword
srv’s setPort:21

set aResList to aFtpManager’s contentsOfServer:srv
aResList
–>  (NSArray) {{kCFFTPResourceLink:”", kCFFTPResourceGroup:”1000″, kCFFTPResourceSize:4096, kCFFTPResourceType:4, kCFFTPResourceOwner:”user@domain.com”, kCFFTPResourceMode:493, kCFFTPResourceModDate:(NSDate) 2011-06-26 15:00:00 +0000, kCFFTPResourceName:”.”}, {kCFFTPResourceLink:”", kCFFTPResourceGroup:”1000″, kCFFTPResourceSize:4096, kCFFTPResourceType:4, kCFFTPResourceOwner:”user@domain.com”, kCFFTPResourceMode:485, kCFFTPResourceModDate:(NSDate) 2012-12-05 15:00:00 +0000, kCFFTPResourceName:”..”}, {kCFFTPResourceLink:”", kCFFTPResourceGroup:”1000″, kCFFTPResourceSize:4096, kCFFTPResourceType:4, kCFFTPResourceOwner:”user@domain.com”, kCFFTPResourceMode:493, kCFFTPResourceModDate:(NSDate) 2008-10-06 15:00:00 +0000, kCFFTPResourceName:”.spamassassin”}, {kCFFTPResourceLink:”", kCFFTPResourceGroup:”1000″, kCFFTPResourceSize:4096, kCFFTPResourceType:4, kCFFTPResourceOwner:”user@domain.com”, kCFFTPResourceMode:493, kCFFTPResourceModDate:(NSDate) 2006-06-09 15:00:00 +0000, kCFFTPResourceName:”autoreply”}, {kCFFTPResourceLink:”", kCFFTPResourceGroup:”1000″, kCFFTPResourceSize:36864, kCFFTPResourceType:4, kCFFTPResourceOwner:”user@domain.com”, kCFFTPResourceMode:493, kCFFTPResourceModDate:(NSDate) 2016-02-29 23:03:00 +0000, kCFFTPResourceName:”awstats”}, {kCFFTPResourceLink:”", kCFFTPResourceGroup:”1000″, kCFFTPResourceSize:4096, kCFFTPResourceType:4, kCFFTPResourceOwner:”user@domain.com”, kCFFTPResourceMode:493, kCFFTPResourceModDate:(NSDate) 2015-02-17 15:00:00 +0000, kCFFTPResourceName:”htpasswd”}, {kCFFTPResourceLink:”", kCFFTPResourceGroup:”1000″, kCFFTPResourceSize:4096, kCFFTPResourceType:4, kCFFTPResourceOwner:”user@domain.com”, kCFFTPResourceMode:493, kCFFTPResourceModDate:(NSDate) 2016-02-29 18:10:00 +0000, kCFFTPResourceName:”log”}, {kCFFTPResourceLink:”", kCFFTPResourceGroup:”1000″, kCFFTPResourceSize:4096, kCFFTPResourceType:4, kCFFTPResourceOwner:”user@domain.com”, kCFFTPResourceMode:448, kCFFTPResourceModDate:(NSDate) 2008-07-05 15:00:00 +0000, kCFFTPResourceName:”mail”}, {kCFFTPResourceLink:”", kCFFTPResourceGroup:”1000″, kCFFTPResourceSize:4096, kCFFTPResourceType:4, kCFFTPResourceOwner:”user@domain.com”, kCFFTPResourceMode:493, kCFFTPResourceModDate:(NSDate) 2015-02-17 15:00:00 +0000, kCFFTPResourceName:”public_html”}, {kCFFTPResourceLink:”", kCFFTPResourceGroup:”1000″, kCFFTPResourceSize:4096, kCFFTPResourceType:4, kCFFTPResourceOwner:”user@domain.com”, kCFFTPResourceMode:493, kCFFTPResourceModDate:(NSDate) 2006-06-09 15:00:00 +0000, kCFFTPResourceName:”script”}, {kCFFTPResourceLink:”", kCFFTPResourceGroup:”1000″, kCFFTPResourceSize:4096, kCFFTPResourceType:4, kCFFTPResourceOwner:”user@domain.com”, kCFFTPResourceMode:493, kCFFTPResourceModDate:(NSDate) 2016-01-19 05:07:00 +0000, kCFFTPResourceName:”xserver_php”}}

set fileNameList to {}
set fileCount to aResList’s |count|()

repeat with i from 0 to (fileCount - 1)
  set anItem to (aResList’s objectAtIndex:i)
  
  
set aFileName to (anItem’s valueForKeyPath:“kCFFTPResourceName”) as string
  
set the end of fileNameList to aFileName
end repeat

fileNameList
–>  {”.”, “..”, “.spamassassin”, “autoreply”, “awstats”, “htpasswd”, “log”, “mail”, “public_html”, “script”, “xserver_php”}

★Click Here to Open This Script