Menu

Skip to content
AppleScriptの穴
  • Home
  • Products
  • Books
  • Docs
  • Events
  • Forum
  • About This Blog
  • License
  • 仕事依頼

AppleScriptの穴

Useful & Practical AppleScript archive. Click '★Click Here to Open This Script' Link to download each AppleScript

カテゴリー: URL

WebView+ボタンを作成 v3.1(URLから読み込み)

Posted on 12月 3, 2018 by Takaaki Naganoya

WkWebViewを動的に生成して、YouTube上の指定ムービーを再生するAppleScriptの改良版です。とくにYouTube限定ではなく、Webコンテンツのブラウズを単体で行うための実験的なScriptです。

# AppleScriptでは、Webブラウザに命令を出せば数行のコマンドで簡単に任意のURLのWebコンテンツを表示させられますが、本ScriptはAppleScriptから他のアプリケーションを介さずに直接Cocoaの機能を呼び出して自前でWebコンテンツを表示させるものです。

NSWindow+NSSplitView+WKWebViewを作成して、Webサーバー上の内容を表示します。v3.0からの改良点は、再生中のWebコンテンツのクリアを明示的に行えるようになったことです。

スクリプトエディタ上で実行し、「OK」ボタンをクリックすると、ウィンドウがクローズし、Webコンテンツの再生も停止します(前バージョンではこれができなかった)。Script MenuおよびScript Debugger上では実行できるもののWebコンテンツ操作はできず、OKボタンも反応しません。

AppleScript Appletとして書き出して実行すると、そのままではインターネットへのアクセスが許可されていないため、表示できません。Info.plistのエントリを書き換える必要があるものと思われます。

再生中のWebコンテンツのクリアについては、US Appleが主催しているCocoa-Dev MLにFritz Andersonが2012年4月11日に投稿した、

On 11 Apr 2012, at 7:51 AM, Koen van der Drift wrote:

> Is there a way to empty or clear a webview when I close the popover,
> so that next time it is opened it doesn't show the previous page? I
> don't want to clear the cache, because when the webview is opened for
> an item that was already selected, it can be loaded from the cache.

What works for me on iOS is having the UIWebView load about:blank. Maybe it works the same in Mac OS.

	— F

が参考になりました。Google検索しても見つからなかったのに、MLの過去ログを漁ってみたら割と簡単に発見。あらかじめMLのログをAppleScriptのロボットでテーマごとに自動フォルダ分けして整理してあったのが効きました。

あとは、実行環境がかなり限定される(Script DebuggerやScript Menuから実行すると、Webコンテンツ操作やボタンのクリックができなくなる)点をなんとかしたいところです。AppleScriptアプレットだとそのまま書き出した状態ではネットワーク接続自体ができない(Info.plist書き換えでなんとか?)とか。

Script Menuからの実行時に無反応になる件をなんとかしたいところです。個人的には、Script Menuが主力実行(ランタイム)環境だと思っているので。

AppleScript名:WebView+ボタンを作成 v3.1(URLから読み込み)
— Created 2018-11-27 by Takaaki Naganoya
— Modified 2018-12-03 by Takaaki Naganoya
— 2018 Piyomaru Software
use AppleScript version "2.5"
use scripting additions
use framework "Foundation"
use framework "AppKit"
use framework "WebKit"

property |NSURL| : a reference to current application’s |NSURL|
property NSString : a reference to current application’s NSString
property NSScreen : a reference to current application’s NSScreen
property NSButton : a reference to current application’s NSButton
property NSWindow : a reference to current application’s NSWindow
property NSSplitView : a reference to current application’s NSSplitView
property NSTextView : a reference to current application’s NSTextView
property NSScrollView : a reference to current application’s NSScrollView
property WKWebView : a reference to current application’s WKWebView
property WKUserScript : a reference to current application’s WKUserScript
property NSURLRequest : a reference to current application’s NSURLRequest
property NSMutableString : a reference to current application’s NSMutableString
property NSWindowController : a reference to current application’s NSWindowController
property NSTitledWindowMask : a reference to current application’s NSTitledWindowMask
property NSRoundedBezelStyle : a reference to current application’s NSRoundedBezelStyle
property NSUTF8StringEncoding : a reference to current application’s NSUTF8StringEncoding
property NSBackingStoreBuffered : a reference to current application’s NSBackingStoreBuffered
property WKUserContentController : a reference to current application’s WKUserContentController
property NSMomentaryLightButton : a reference to current application’s NSMomentaryLightButton
property WKWebViewConfiguration : a reference to current application’s WKWebViewConfiguration
property NSScreenSaverWindowLevel : a reference to current application’s NSScreenSaverWindowLevel
property NSWindowStyleMaskResizable : a reference to current application’s NSWindowStyleMaskResizable
property NSWindowStyleMaskMiniaturizable : a reference to current application’s NSWindowStyleMaskMiniaturizable
property WKUserScriptInjectionTimeAtDocumentEnd : a reference to current application’s WKUserScriptInjectionTimeAtDocumentEnd

property windisp : false
property wController : false

set aWidth to 1100
set aHeight to 700

set aTitle to "WkWebView test2"
set aButtonMSG to "OK"

set aURL to "https://www.youtube.com/embed/GP_tVXTYdmY?autoplay=1&hd=1"

set paramObj to {aWidth, aHeight, aTitle, aURL, aButtonMSG, "600"}
my performSelectorOnMainThread:"dispWebView:" withObject:(paramObj) waitUntilDone:true

on dispWebView:paramObj
  set my windisp to false
  
copy (paramObj as list) to {aWidth, aHeight, aTitle, tmpURL, aButtonMSG, timeOutSecs}
  
  
set (my windisp) to true
  
  
set aWidth to aWidth as integer
  
set aHeight to aHeight as integer
  
set tmpURL to tmpURL as string
  
  
–WebViewをつくる
  
set aConf to WKWebViewConfiguration’s alloc()’s init()
  
  
–指定URLのJavaScriptをFetch
  
set jsSource to my fetchJSSourceString(tmpURL)
  
set userScript to WKUserScript’s alloc()’s initWithSource:jsSource injectionTime:(WKUserScriptInjectionTimeAtDocumentEnd) forMainFrameOnly:true
  
set userContentController to WKUserContentController’s alloc()’s init()
  
userContentController’s addUserScript:(userScript)
  
aConf’s setUserContentController:userContentController
  
  
set aWebView to WKWebView’s alloc()’s initWithFrame:(current application’s NSMakeRect(0, 0, aWidth, aHeight – 100)) configuration:aConf
  
aWebView’s setNavigationDelegate:me
  
aWebView’s setUIDelegate:me
  
aWebView’s setTranslatesAutoresizingMaskIntoConstraints:true
  
  
set bURL to |NSURL|’s URLWithString:tmpURL
  
set aReq to NSURLRequest’s requestWithURL:bURL
  
aWebView’s loadRequest:aReq –Webコンテンツのローディング
  
  
  
–Buttonをつくる
  
set bButton to (NSButton’s alloc()’s initWithFrame:(current application’s NSMakeRect(0, 0, aWidth, 40)))
  
bButton’s setTitle:aButtonMSG
  
bButton’s setTarget:me
  
bButton’s setAction:("clicked:")
  
bButton’s setKeyEquivalent:(return)
  
  
–SplitViewをつくる
  
set aSplitV to NSSplitView’s alloc()’s initWithFrame:(current application’s NSMakeRect(0, 0, aWidth, aHeight))
  
aSplitV’s setVertical:false
  
  
aSplitV’s addSubview:aWebView
  
aSplitV’s addSubview:bButton
  
aSplitV’s setNeedsDisplay:true
  
  
set aWin to makeWinWithView(aSplitV, aWidth, aHeight, aTitle, 1.0)
  
  
–NSWindowControllerを作ってみた
  
set my wController to NSWindowController’s alloc()
  
my (wController’s initWithWindow:aWin)
  
  
my (wController’s showWindow:me)
  
  
set aCount to (timeOutSecs as string as number) * 10 –timeout seconds * 10
  
repeat aCount times
    if (my windisp) = false then
      exit repeat
    end if
    
delay 0.1
  end repeat
  
  
–Stop Web View Action
  
set bURL to |NSURL|’s URLWithString:"about:blank"
  
set bReq to NSURLRequest’s requestWithURL:bURL
  
aWebView’s loadRequest:bReq
  
  
my closeWin:bButton
end dispWebView:

–Button Clicked Event Handler
on clicked:aSender
  set (my windisp) to false
  
my closeWin:aSender
end clicked:

–make Window for Input
on makeWinWithView(aView, aWinWidth, aWinHeight, aTitle, alphaV)
  set aScreen to NSScreen’s mainScreen()
  
set aFrame to {{0, 0}, {aWinWidth, aWinHeight}}
  
set aBacking to NSTitledWindowMask –NSBorderlessWindowMask
  
set aDefer to NSWindowStyleMaskMiniaturizable
  
  
— Window
  
set aWin to NSWindow’s alloc()
  (
aWin’s initWithContentRect:aFrame styleMask:aBacking backing:aDefer defer:false screen:aScreen)
  
  
aWin’s setTitle:aTitle
  
aWin’s setDelegate:me
  
aWin’s setDisplaysWhenScreenProfileChanges:true
  
aWin’s setHasShadow:true
  
aWin’s setIgnoresMouseEvents:false
  
aWin’s setLevel:(NSScreenSaverWindowLevel)
  
aWin’s setOpaque:false
  
aWin’s setAlphaValue:alphaV –append
  
aWin’s setReleasedWhenClosed:true
  
aWin’s |center|()
  
  
— Set Custom View
  
aWin’s setContentView:aView
  
  
return aWin
end makeWinWithView

–close win
on closeWin:aSender
  set tmpWindow to aSender’s |window|()
  
  
repeat with n from 10 to 1 by -1
    (tmpWindow’s setAlphaValue:n / 10)
    
delay 0.02
  end repeat
  
  
my wController’s |close|()
end closeWin:

on viewDidLoad:aNotification
  return true
end viewDidLoad:

on fetchJSSourceString(aURL)
  set jsURL to |NSURL|’s URLWithString:aURL
  
set jsSourceString to NSString’s stringWithContentsOfURL:jsURL encoding:(NSUTF8StringEncoding) |error|:(missing value)
  
return jsSourceString
end fetchJSSourceString

★Click Here to Open This Script 

Posted in URL | Tagged 10.11savvy 10.12savvy 10.13savvy 10.14savvy NSBackingStoreBuffered NSButton NSMomentaryLightButton NSMutableString NSRoundedBezelStyle NSScreen NSSplitView NSString NSTitledWindowMask NSURL NSURLRequest NSUTF8StringEncoding NSWindow NSWindowController WKUserContentController WKUserScript WKUserScriptInjectionTimeAtDocumentEnd WKWebView WKWebViewConfiguration | 1 Comment

WebView+ボタンを作成 v3(URLから読み込み)

Posted on 11月 28, 2018 by Takaaki Naganoya

WkWebViewを動的に生成して、YouTube上の指定ムービーを再生するAppleScriptです。

# AppleScriptでは、Webブラウザに命令を出せば数行のコマンドで簡単に任意のURLのWebコンテンツを表示させられますが、本ScriptはAppleScriptから他のアプリケーションを介さずに直接Cocoaの機能を呼び出して自前でWebコンテンツを表示させるものです

NSWindow+NSSplitView+WKWebViewを作成して、Webサーバー上の内容を表示します。WkWebViewでWebの内容を表示させるのにかなり苦労しましたが、Objective-Cのサンプル数本を比較してテストし実現しました。

WkWebViewでコンテンツを表示するのにこんなに大変だと思いませんでしたが、これができるようになったおかげで、AppleScriptの処理結果のURLを(Webブラウザを利用せずに)プレビューできます。

グラフ描画のプログラムがJavaScript+WebViewで作られているケースが多々あるため、凝ったインタラクティブなグラフの表示を行ったり画像やPDFに出力できるようにもなったわけで、たいへんけっこうなことです。


▲Auto StartでYouTube上のムービーの再生を開始する

まだ解決できていないのは、YouTubeのムービー再生が始まるとウィンドウをクローズしてAppleScriptの実行が終了していても再生中の音声が停止しないあたりでしょうか。メモリ上からWebブラウザのオブジェクトを明示的にパージする方法についてはまだ実装できていません。

あと、Script Debugger上で動かすと、WkWebView上のコンテンツの操作(YouTube映像の再生コントロールとか)ができなくなります。ねんのため。

AppleScript名:WebView+ボタンを作成 v3(URLから読み込み)
— Created 2018-11-27 by Takaaki Naganoya
— 2018 Piyomaru Software
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"
use framework "AppKit"
use framework "WebKit"

property |NSURL| : a reference to current application’s |NSURL|
property NSColor : a reference to current application’s NSColor
property NSString : a reference to current application’s NSString
property NSScreen : a reference to current application’s NSScreen
property NSButton : a reference to current application’s NSButton
property NSWindow : a reference to current application’s NSWindow
property NSSplitView : a reference to current application’s NSSplitView
property NSTextView : a reference to current application’s NSTextView
property NSScrollView : a reference to current application’s NSScrollView
property WKWebView : a reference to current application’s WKWebView
property WKUserScript : a reference to current application’s WKUserScript
property NSURLRequest : a reference to current application’s NSURLRequest
property NSMutableString : a reference to current application’s NSMutableString
property NSWindowController : a reference to current application’s NSWindowController
property NSTitledWindowMask : a reference to current application’s NSTitledWindowMask
property NSRoundedBezelStyle : a reference to current application’s NSRoundedBezelStyle
property NSUTF8StringEncoding : a reference to current application’s NSUTF8StringEncoding
property NSBackingStoreBuffered : a reference to current application’s NSBackingStoreBuffered
property WKUserContentController : a reference to current application’s WKUserContentController
property NSMomentaryLightButton : a reference to current application’s NSMomentaryLightButton
property WKWebViewConfiguration : a reference to current application’s WKWebViewConfiguration
property NSScreenSaverWindowLevel : a reference to current application’s NSScreenSaverWindowLevel
property WKUserScriptInjectionTimeAtDocumentEnd : a reference to current application’s WKUserScriptInjectionTimeAtDocumentEnd

property windisp : false
property wController : false

set aWidth to 1100
set aHeight to 600

set aTitle to "WkWebView test"
set aButtonMSG to "OK"
–set aURL to "https://www.youtube.com/watch?v=GP_tVXTYdmY&autoplay=1&hd=1"
set aURL to "https://www.youtube.com/embed/GP_tVXTYdmY?autoplay=1&hd=1"

set paramObj to {aWidth, aHeight, aTitle, aURL, aButtonMSG, "600"}
my performSelectorOnMainThread:"dispWebView:" withObject:(paramObj) waitUntilDone:true

on dispWebView:paramObj
  set my windisp to false
  
copy (paramObj as list) to {aWidth, aHeight, aTitle, tmpURL, aButtonMSG, timeOutSecs}
  
  
set (my windisp) to true
  
  
set aWidth to aWidth as integer
  
set aHeight to aHeight as integer
  
set tmpURL to tmpURL as string
  
  
–WebViewをつくる
  
set aConf to WKWebViewConfiguration’s alloc()’s init()
  
set jsSource to my fetchJSSourceString(tmpURL)
  
set userScript to WKUserScript’s alloc()’s initWithSource:jsSource injectionTime:(WKUserScriptInjectionTimeAtDocumentEnd) forMainFrameOnly:true
  
set userContentController to WKUserContentController’s alloc()’s init()
  
userContentController’s addUserScript:(userScript)
  
aConf’s setUserContentController:userContentController
  
  
set aWebView to WKWebView’s alloc()’s initWithFrame:(current application’s NSMakeRect(0, 0, aWidth, aHeight – 100)) configuration:aConf –フレームの大きさは根拠レス
  
aWebView’s setNavigationDelegate:me
  
aWebView’s setUIDelegate:me
  
aWebView’s setTranslatesAutoresizingMaskIntoConstraints:true
  
  
set bURL to |NSURL|’s URLWithString:tmpURL
  
set aReq to NSURLRequest’s requestWithURL:bURL
  
aWebView’s loadRequest:aReq
  
  
  
–Buttonをつくる
  
set bButton to (NSButton’s alloc()’s initWithFrame:(current application’s NSMakeRect(0, 0, aWidth, 40)))
  
bButton’s setTitle:aButtonMSG
  
bButton’s setTarget:me
  
bButton’s setAction:("clicked:")
  
bButton’s setKeyEquivalent:(return)
  
  
–SplitViewをつくる
  
set aSplitV to NSSplitView’s alloc()’s initWithFrame:(current application’s NSMakeRect(0, 0, aWidth, aHeight))
  
aSplitV’s setVertical:false
  
  
aSplitV’s addSubview:aWebView
  
aSplitV’s addSubview:bButton
  
aSplitV’s setNeedsDisplay:true
  
  
set aWin to makeWinWithView(aSplitV, aWidth, aHeight, aTitle, 1.0)
  
  
–NSWindowControllerを作ってみた
  
set my wController to NSWindowController’s alloc()
  
my (wController’s initWithWindow:aWin)
  
  
my (wController’s showWindow:me)
  
  
set aCount to (timeOutSecs as string as number) * 10 –timeout seconds * 10
  
repeat aCount times
    if (my windisp) = false then
      exit repeat
    end if
    
delay 0.1
  end repeat
  
  
–Purge Web Objects (not success)
  
set aWebView to ""
  
set userContentController to ""
  
set aConf to ""
  
set userScript to ""
  
set my wController to ""
  
  
my closeWin:bButton
end dispWebView:

–Button Clicked Event Handler
on clicked:aSender
  set (my windisp) to false
  
my closeWin:aSender
end clicked:

–make Window for Input
on makeWinWithView(aView, aWinWidth, aWinHeight, aTitle, alphaV)
  set aScreen to NSScreen’s mainScreen()
  
set aFrame to {{0, 0}, {aWinWidth, aWinHeight}}
  
set aBacking to NSTitledWindowMask –NSBorderlessWindowMask
  
set aDefer to NSBackingStoreBuffered
  
  
— Window
  
set aWin to NSWindow’s alloc()
  (
aWin’s initWithContentRect:aFrame styleMask:aBacking backing:aDefer defer:false screen:aScreen)
  
  
aWin’s setTitle:aTitle
  
aWin’s setDelegate:me
  
aWin’s setDisplaysWhenScreenProfileChanges:true
  
aWin’s setHasShadow:true
  
aWin’s setIgnoresMouseEvents:false
  
aWin’s setLevel:(NSScreenSaverWindowLevel)
  
aWin’s setOpaque:false
  
aWin’s setAlphaValue:alphaV –append
  
aWin’s setReleasedWhenClosed:true
  
aWin’s |center|()
  
  
— Set Custom View
  
aWin’s setContentView:aView
  
  
return aWin
end makeWinWithView

–close win
on closeWin:aSender
  set tmpWindow to aSender’s |window|()
  
  
repeat with n from 10 to 1 by -1
    (tmpWindow’s setAlphaValue:n / 10)
    
delay 0.02
  end repeat
  
  
my wController’s |close|()
end closeWin:

on viewDidLoad:aNotification
  return true
end viewDidLoad:

on fetchJSSourceString(aURL)
  set jsURL to |NSURL|’s URLWithString:aURL
  
set jsSourceString to NSString’s stringWithContentsOfURL:jsURL encoding:(NSUTF8StringEncoding) |error|:(missing value)
  
return jsSourceString
end fetchJSSourceString

★Click Here to Open This Script 

Posted in Internet URL | Tagged 10.12savvy 10.13savvy 10.14savvy NSButton NSMomentaryLightButton NSMutableString NSScreen NSScrollView NSSplitView NSString NSTextView NSURL NSURLRequest NSWindow NSWindowController WKUserContentController WKUserScript WKWebView WKWebViewConfiguration | 1 Comment

teratailの指定IDのユーザー情報を取得する_curl

Posted on 11月 27, 2018 by Takaaki Naganoya

プログラミング系質問サイトteratailのREST APIを呼び出して、指定ユーザー名の情報を取得するAppleScriptです。

TeratailのREST APIは、タグ、ユーザー、質問の3ジャンルの情報取得を行えるように整備されており、特定カテゴリ(タグで分類)の新規質問が投稿されたかどうかを定期的に確認するようなAppleScriptを作って運用することもできます(そこまでやっていないですけれども)。

REST API呼び出しにはNSURLConnectionからNSURLSessionに移行していますが、どうもNSURLSessionだと呼び出せない(AppleScriptからの呼び出し処理が完了しない)サービスがあったりするので、結局shellのcurlコマンドを呼び出すのが手短にすむケースが多いようです。

Teratailの場合も、NSURLSessionで呼び出せるAPIもあれば、結果が返ってこないAPIもあり、NSURLConnectionよりも使い勝手がよくないと感じています(個人の感想です)。

このあたり、将来的なmacOSのアップデートでNSURLConnectionが使えなくなる日が来るのかもしれませんが、curlコマンドを使うように集約するべきなのか、NSURLSessionで書き換えるべきなのか悩ましいところです。

AppleScript名:teratailの指定IDのユーザー情報を取得する_curl
— Created 2018-11-26 by Takaaki Naganoya
— 2018 Piyomaru Software
use AppleScript version "2.5"
use scripting additions
use framework "Foundation"

property |NSURL| : a reference to current application’s |NSURL|
property NSString : a reference to current application’s NSString
property NSURLSession : a reference to current application’s NSURLSession
property NSURLQueryItem : a reference to current application’s NSURLQueryItem
property NSJSONSerialization : a reference to current application’s NSJSONSerialization
property NSURLComponents : a reference to current application’s NSURLComponents
property NSMutableDictionary : a reference to current application’s NSMutableDictionary
property NSMutableURLRequest : a reference to current application’s NSMutableURLRequest
property NSUTF8StringEncoding : a reference to current application’s NSUTF8StringEncoding
property NSURLSessionConfiguration : a reference to current application’s NSURLSessionConfiguration

set aUserRes to searchOneUserByDisplayName("Piyomaru") of me
–> {meta:{limit:20, message:"success", hit_num:1, total_page:1, page:1}, users:{{score:43, photo:"https://teratail.storage.googleapis.com/uploads/avatars/u6/66639/MSIS21by_thumbnail.jpg", display_name:"Piyomaru"}}}

on searchOneUserByDisplayName(aName)
  set aRec to {q:aName}
  
set reqURLStr to "https://teratail.com/api/v1/users/search"
  
set bURL to retURLwithParams(reqURLStr, aRec) of me
  
  
set tmpData to (do shell script "curl -X GET \"" & bURL & "\"")
  
set jsonString to NSString’s stringWithString:tmpData
  
set jsonData to jsonString’s dataUsingEncoding:(NSUTF8StringEncoding)
  
set aJsonDict to NSJSONSerialization’s JSONObjectWithData:jsonData options:0 |error|:(missing value)
  
if aJsonDict = missing value then return false
  
return (aJsonDict as record)
end searchOneUserByDisplayName

on retURLwithParams(aBaseURL, aRec)
  set aDic to NSMutableDictionary’s dictionaryWithDictionary:aRec
  
set aKeyList to (aDic’s allKeys()) as list
  
set aValList to (aDic’s allValues()) as list
  
set aLen to length of aKeyList
  
  
set qList to {}
  
repeat with i from 1 to aLen
    set aName to contents of item i of aKeyList
    
set aVal to contents of item i of aValList
    
set the end of qList to (NSURLQueryItem’s queryItemWithName:aName value:aVal)
  end repeat
  
  
set aComp to NSURLComponents’s alloc()’s initWithString:aBaseURL
  
aComp’s setQueryItems:qList
  
set aURL to (aComp’s |URL|()’s absoluteString()) as text
  
  
return aURL
end retURLwithParams

★Click Here to Open This Script 

Posted in Network Record REST API shell script URL | Tagged 10.11savvy 10.12savvy 10.13savvy 10.14savvy NSJSONSerialization NSMutableDictionary NSString NSURL NSURLComponents NSURLQueryItem | 1 Comment

システム環境設定>プライバシーの「オートメーション」項目の表示

Posted on 11月 19, 2018 by Takaaki Naganoya

macOS 10.14, Mojaveでにわかに重要になってきた「システム環境設定」>セキュリティとプライバシー>プライバシー>オートメーション 項目の表示を行うAppleScriptです。

もともと、システム環境設定には指定のPreferences Paneを表示する機能が用意されているのですが、最近このシステム環境設定上の項目の移設や統廃合がメジャーバージョンアップごとに行われており、そうした変更にAppleScript系の機能の変更が間に合っていません。

tell application "System Preferences"
  tell pane "com.apple.preference.security"
    get name of every anchor
  end tell
end tell
–> {"Privacy_Reminders", "Privacy_SystemServices", "Privacy_Calendars", "Firewall", "Privacy_Assistive", "Privacy_LinkedIn", "Privacy_LocationServices", "Privacy_Contacts", "General", "Privacy_Diagnostics", "Advanced", "Privacy_Accessibility", "Privacy_Camera", "FDE", "Privacy", "Privacy_AllFiles", "Privacy_Microphone"}

★Click Here to Open This Script 


▲「セキュリティ」項目以下の各anchor。赤く塗った項目はAppleScript用語辞書に用語が用意されていないもの

とくに、この重要な「オートメーション」項目を示す予約語がAppleScript用語辞書に登録されておらず、「フルディスクアクセス」の予約語が用意されているあたり、チェックもれで抜けたものと思われます。

きちんと予約語が存在する項目については、

tell application "System Preferences"
  activate
  
reveal anchor "Privacy_LocationServices" of pane "com.apple.preference.security"
end tell

★Click Here to Open This Script 

のようにして表示できるのですが、前述のとおり「オートメーション」項目の予約語が用意されていません。

そこで、別途URL Event経由で表示させる方法を見つけたのでまとめておきました。

非同期のURL Eventなので、「オートメーション」項目が表示し終わったかどうかといった確認は一切ありません。普通にApple Event経由で実行できるのであれば、表示し終わったという実行結果の確認まで行えるのですが、、、、

AppleScript名:システム環境設定でオートメーションのタブを表示させる
open location "x-apple.systempreferences:com.apple.preference.security?Privacy_Automation"

★Click Here to Open This Script 

Posted in Sandbox System URL | Tagged 10.14savvy System Preferences | Leave a comment

指定URLをロード_WKWebView版 v2

Posted on 11月 4, 2018 by Takaaki Naganoya

指定のURLを読み込んでページのタイトルを取得するAppleScriptです。

オリジナルはControl+Command+Rの操作が必要でしたが、本バージョンでは必要ありません。ただ、Script Debuggerとの相性はよろしくないようで。

AppleScript名:指定URLをロード_WKWebView版 v2
— Created 2015-09-16 by Takaaki Naganoya
— 2015 Piyomaru Software
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"
use framework "WebKit"

property theWebView : missing value

set aURL to "http://www.apple.com/jp/shop/browse/home/specialdeals/mac"

my performSelectorOnMainThread:"getPage:" withObject:(aURL) waitUntilDone:true

set aTitle to (theWebView)’s title()
return aTitle as text
–>  "Mac整備済製品 – Apple(日本)"

–Download the URL page to WkWebView
on getPage:aURL
  set thisURL to current application’s |NSURL|’s URLWithString:aURL
  
set theRequest to current application’s NSURLRequest’s requestWithURL:thisURL
  
  
set aConf to current application’s WKWebViewConfiguration’s alloc()’s init()
  
  
set (my theWebView) to current application’s WKWebView’s alloc()’s initWithFrame:(current application’s NSMakeRect(0, 0, 800, 600)) configuration:aConf –フレームの大きさは根拠レス
  (
my theWebView)’s setNavigationDelegate:me
  (
my theWebView)’s loadRequest:theRequest
  
  
set waitLoop to 100 * 60 –60 seconds
  
  
set hitF to false
  
repeat waitLoop times
    set aLoadF to ((my theWebView)’s estimatedProgress()) as number
    
if aLoadF = 1.0 then
      set hitF to true
      
exit repeat
    end if
    
current application’s NSThread’s sleepForTimeInterval:(0.01)
    
–delay 0.1
  end repeat
  
  
return hitF
end getPage:

★Click Here to Open This Script 

Posted in URL | Tagged 10.11savvy 10.12savvy 10.13savvy NSThread NSURL NSURLRequest WKWebView WKWebViewConfiguration | Leave a comment

AppleScript系機能の実装がおかしなGoogle Chrome

Posted on 11月 2, 2018 by Takaaki Naganoya

Google Chromeは自動処理の部品に使ってはいけない

Google ChromeをAppleScriptでコントロールすることは、個人的にはほとんどありません。日常的に利用するのは、自動処理への対応機能を持ち信頼できるアプリケーションが中心です。

必要な機能がなかったり、自動処理と相性がよくなかったりするためです。使用中にGoogle App Storeのダイアログなどが表示されることがあるので、自動処理中にやられると処理が止まってしまいます。これは、自動処理の仕組みを組み立てる上で絶対にあってはならない仕様です。

そのため、Google ChromeをAppleScriptで動かす場合は、途中でGoogle Chrome側の都合で勝手に処理が止まっても腹が立たない程度の、あまり重要ではない処理であるとか、とても短くて簡単な基礎的なマクロ処理に限定されます。

1日中タイマーにのっとって定時に指定のWebサイトに、複数のユーザーIDやパスワードで繰り返しログインを行い、指定のメニューに移動してデータをダウンロードするような処理(実際にAppleScript+Safariで動かしています)にGoogle Chromeを使うのは自殺行為です。

比較的短めの、

「写真.appで選択中の写真をOCRで文字認識して『戦場の絆』のリプレイIDを取得し、リプレイムービー検索ページにリプレイIDを突っ込み、YouTube上のリプレイムービーのURLを取得。YouTube上でリプレイを再生する」

といった程度の処理でも、途中でGoogle App Storeのダイアログが表示されたら、処理が途切れてそれっきり。コマンドを1つ実行するたびにGoogle App Storeのダイアログを閉じたり無効化する処理を入れれば不可能ではないかもしれませんが、無人の環境で放置するのは無理です。

かといって、Safariの仕様が自動処理に向いているかと言われれば、そういうわけでもありません。ファイルのダウンロード検出をSafari側で行えないなど、自動処理専用のWebブラウザがあったらいいのに、と思うところです(Folder ActionやFSEventsと組み合わせて、非同期処理でダウンロード検出を行なっています)。ただ、Google Chromeのような致命的な動作を行わない点で選択肢になることでしょう。

Google Chromeには、表示中のページのHTMLソースを取得するという、基本的かつないと困る機能が実装されていないのも困りものです。

Google Chromeは実装がおかしい

Google Chromeは一応JavaScriptを実行するための「execute」コマンドを備えているので、JavaScriptのコマンド経由でWebコンテンツへのアクセスや、フォームへのアクセスはできるようになっています。

ただし、肝心の「指定URLをオープンする」という部分の実装がまともに行われていません。

Google Chromeで指定URLをオープンするAppleScriptでよく見かける記述例は、

tell application "Google Chrome"
  open location "http://piyocast.com/as/"
end tell

★Click Here to Open This Script 

のようなものですが、そもそもこの「open location」はAppleScriptの標準命令であり、Google Chromeが持っているコマンドではありません。open locationは指定URLのURLスキームに対応しているアプリケーションに処理が委ねられます。tellブロックを外すと、OSでデフォルト指定されているアプリケーションで処理されます。Google Chromeへのtellブロック内で実行しているので、Google ChromeにURLが転送されているだけです。

Google Chromeの機能を使って指定URLをオープンしようとすると、

tell application "Google Chrome"
  set newWin to make new window
  
tell newWin
    tell tab 1
      set URL to "http://piyocast.com/as/"
    end tell
  end tell
end tell

★Click Here to Open This Script 

のようになります。

一番ひどいのは、Google Chromeのopen命令。これ、

open v : Open a document.
  open list of file : The file(s) to be opened.

用語辞書のとおりに書いてもエラーになります。

そのため、ローカルのファイルをオープンさせようとしても、openコマンドは使えません。fileを与えてもaliasを与えても、AppleScript用語辞書の記述のとおりにfileのlistを与えてもエラーになります。

結論としては、ローカルのfileをオープンする際には、file pathをURL形式に変換して、そのURLをopen locationでオープンするか、tabのURL属性をfile URLに書き換えて表示させることになります。

set anAlias to choose file

tell application "Finder"
  set aURL to URL of anAlias
end tell

tell application "Google Chrome"
  open location aURL
end tell

★Click Here to Open This Script 

set anAlias to choose file

tell application "Finder"
  set aURL to URL of anAlias
end tell

tell application "Google Chrome"
  tell window 1
    tell active tab
      set URL to aURL
    end tell
  end tell
end tell

★Click Here to Open This Script 

Finder経由で目的のファイルをGoogle Chromeというアプリケーションを利用してオープンする、という記述もできますが、ここでもそのままapplication “Google Chrome”と書くとエラーになり、「path to application “Google Chrome”」という回避的な記述が必要になってしまうのでありました。

–By @fixdot on Twitter
set anAlias to choose file

tell application "Finder"
  open anAlias using (path to application "Google Chrome")
end tell

★Click Here to Open This Script 

ただ、ここで書いているのは「自動処理を行うユーザー環境では」ということであって、Google Chromeは自動処理にさえ使わなければ、それなりによくできたいいソフトウェアだと思います。

ほかにもいろいろ落とし穴が

とくに、Google Chromeにかぎらず、macOS内には自動処理の妨げとなる設定項目がいくつか存在しています。デフォルトのまま使うといろいろ問題が出るので、真っ先に設定を変更しています。

Mac miniにBluetooth外付けキーボードを接続した状態で、ファンクションキーをメディアキーに指定して運用していると、iTunesから不定期にダイアログ表示されるため、ファンクションキーはあくまでファンクションキーとして設定しないとダメです(MacBook Proなど本体にキーボードが搭載されているモデルはこのかぎりではありません)。

また、同様にMac miniでヘッドレス(画面なし)運用をしている際に、キーボードやマウス/トラックパッドを接続していない状態で警告するように初期設定されていますが、

 「起動時にキーボードが検出されない場合はBluetooth設定アシスタントを開く」
 「起動時にマウスまたはトラックパッドが検出されない場合はBluetooth設定アシスタントを開く」

の2つのチェックボックスをオフにしておく必要があります。

com.google.Chrome

Posted in file URL | Tagged 10.11savvy 10.12savvy 10.13savvy Google Chrome | Leave a comment

NSURLからBookmarkを作成するじっけん

Posted on 10月 3, 2018 by Takaaki Naganoya

NSURLからbookmarkDataを作成、あるいはbookmarkDataからNSURLを復元する実験を行うAppleScriptです。

POSIX pathのファイルパスをplistに保存するような場合に、元のファイルの場所が変わったり名前が変わったりすると、追跡できなくなってしまいます。

 「aliasをpropertyに保存していた時代には、自動で追跡してもらえたんだけどなー」

そのため、plistからPOSIX pathを取り出したあとは逐一「本当にそのパスにファイルが存在するか」をチェックしていたのですが、

 「もっといいものがあるよ」

と教えていただいたのが、このbookmarkDataです。

NSURL(filePathのほう)をbookmarkDataにすると、元のファイルの名前が変わったり場所(パス)が変わったりしても追跡してくれます。ファイルのノード番号をベースに追跡しているのでしょうか。とにかく、追跡してもらえるのはいいことです。

本AppleScriptでは、POSIX pathをNSURLを経由してboookmarkDataに変換し、オリジナルのファイルをリネームして、bookmarkDataからNSURL–> POSIX pathを取得。得られたパスがリネーム後のものと同じかどうかをチェックするものです。

テストを行った範囲では、想定どおりに使えているようです。

AppleScript名:POSIX path–>Bookmark & bookmark –> POSIX path.scptd
—
–  Created by: Takaaki Naganoya
–  Created on: 2018/10/02
—
–  Copyright © 2018 Piyomaru Software, All Rights Reserved
—
use AppleScript version "2.4" — Yosemite (10.10) or later
use framework "Foundation"
use scripting additions

property |NSURL| : a reference to current application’s |NSURL|
property NSString : a reference to current application’s NSString
property NSFileManager : a reference to current application’s NSFileManager
property NSURLBookmarkResolutionWithoutUI : a reference to current application’s NSURLBookmarkResolutionWithoutUI

set aFile to POSIX path of (choose file)
–> "/Users/me/Desktop/9E37BC8B-3920-4844-A6D1-0DCA183D744D.png"

–POSIX path –> URL –> Bookmark
set aURL to |NSURL|’s fileURLWithPath:aFile
set aBookmarkData to aURL’s bookmarkDataWithOptions:0 includingResourceValuesForKeys:(missing value) relativeToURL:(missing value) |error|:(missing value)

–Rename Original File
set fRes to renameFileItem(aFile, "test_test_test") of me
if fRes = false then error "Error: File already exists."

–Bookmark –> URL –> POSIX path
set bURL to |NSURL|’s URLByResolvingBookmarkData:aBookmarkData options:(NSURLBookmarkResolutionWithoutUI) relativeToURL:(missing value) bookmarkDataIsStale:(missing value) |error|:(missing value)
set bPath to (bURL’s |path|()) as string
–> "/Users/me/Desktop/test_test_test.png"

–File Rename Routine
on renameFileItem(aPOSIX, newName)
  set theNSFileManager to NSFileManager’s defaultManager()
  
set POSIXPathNSString to NSString’s stringWithString:(aPOSIX)
  
  
–Make New File Path
  
set anExtension to POSIXPathNSString’s pathExtension()
  
set newPath to (POSIXPathNSString’s stringByDeletingLastPathComponent()’s stringByAppendingPathComponent:newName)’s stringByAppendingPathExtension:anExtension
  
  
–Rename
  
if theNSFileManager’s fileExistsAtPath:newPath then
    return false
  else
    set theResult to theNSFileManager’s moveItemAtPath:POSIXPathNSString toPath:newPath |error|:(missing value)
    
if (theResult as integer = 1) then
      return (newPath as string)
    else
      return false
    end if
  end if
end renameFileItem

★Click Here to Open This Script 

Posted in file URL | Tagged 10.11savvy 10.12savvy 10.13savvy NSFileManager NSString NSURL NSURLBookmarkResolutionWithoutUI | 1 Comment

ZipZap frameworkを使ってZipアーカイブ内の情報を取得しファイルタイプごとに対応出力

Posted on 9月 29, 2018 by Takaaki Naganoya

オープンソースのZipZap frameworkを用いて、指定Zipアーカイブ内の情報を取得し、ファイルタイプごとに対応した出力を行うAppleScriptです。

–> ZipZap.framework(To ~/Library/Frameworks/)

客観的に動作内容を書くとわかりにくいですが、「目的」を書くと非常にわかりやすい部品です。

「バンドル内にパスワード付きのZipアーカイブ化したAppleScript書類を入れておいて、実行時にアーカイブからAppleScriptのソースを取り出して実行する」

というのが、その用途です。

プレーンテキストと画像を取り出せるような記述になっていますが、それらはあくまでも実験用で、AppleScript書類のソースを取り出すのが本来の目的です。

AppleScriptでGUIアプリケーションを作成したときに、GUIまわりは普通にXcode上で記述して(あるいは、外部エディタ上で記述して)おきますが、外部のアプリケーションをコントロールするコードは、実行専用の.scpt形式でかつ書き込み禁止状態にしてバンドル内に入れておいたりします(SandBox対応のため)。

ただ、さまざまな要求を満たすようにGUIアプリケーション操作用のScriptを呼び出そうとすると、load scriptで読み込んで実行させていたのでは、都合がよくない場合があります(何らかのキーを長押しすると実行を停止できるとか、システム内の別の要素……たとえばCPUの温度が上がりすぎたら停止させるとか)。

load scriptで実行すると、

(1)途中で実行停止しにくい
(2)セキュリティ上の制約がきつい(とくにGUI Scriptingまわり)
(3)スクリプトエディタ上で動かしていた時と挙動が変わる箇所がある(Finder上のselectionを取得していた場合とか)

といった問題がありますが、OSAScriptView上にAppleScriptのソースを展開して実行すると、これらの問題を解決できる一方、ソースが見える形式でアプリケーションバンドル内に入れるのはためらわれます。

その問題を解決するために書いたものです。展開したデータをファイル出力せず、すべて変数上で(オンメモリで)処理できるので都合がいいです。

Mac App Storeで販売するアプリケーションでこの部品を使ったことはありませんが、客先に納品するアプリケーションでは使えるといったところでしょうか。ここまで手の込んだプログラムだとリジェクトできないとは思っています(もっとレベルの低い指摘しか来ないので)。

AppleScript名:ZipZap frameworkを使ってZipアーカイブ内の情報を取得してファイルタイプごとに対応出力 v2
— Created 2018-09-28 by Takaaki Naganoya
— 2018 Piyomaru Software
use AppleScript version "2.5"
use scripting additions
use framework "Foundation"
use framework "OSAKit"
use framework "ZipZap" –https://github.com/pixelglow/zipzap
use bPlus : script "BridgePlus" –https://www.macosxautomation.com/applescript/apps/BridgePlus.html

property |NSURL| : a reference to current application’s |NSURL|
property NSArray : a reference to current application’s NSArray
property NSString : a reference to current application’s NSString
property NSImage : a reference to current application’s NSImage
property ZZArchive : a reference to current application’s ZZArchive
property OSAScript : a reference to current application’s OSAScript
property NSPredicate : a reference to current application’s NSPredicate
property NSUTF8StringEncoding : a reference to current application’s NSUTF8StringEncoding
property NSURLTypeIdentifierKey : a reference to current application’s NSURLTypeIdentifierKey

set aPassword to "piyomarusoftware"
set aPath to POSIX path of (choose file of type {"public.zip-archive"})
set aList to extractArchive(aPath, aPassword) of me

on extractArchive(aPath, aPass)
  set oldArchive to ZZArchive’s archiveWithURL:(|NSURL|’s fileURLWithPath:aPath) |error|:(missing value)
  
set aList to oldArchive’s entries() as list
  
  
set outList to {}
  
  
repeat with i in aList
    set encF to i’s encrypted() as boolean
    
    
set aFileName to i’s fileName()
    
    
if (aFileName as string) does not start with "__MACOSX/" then
      set aExt to (aFileName’s pathExtension()) as string
      
set aUTI to my retFileFormatUTI(aExt)
      
set aData to (i’s newDataWithError:(missing value)) –Uncompressed raw data
      
      
if aData = missing value then
        set aData to (i’s newDataWithPassword:(aPass) |error|:(missing value))
        
if aData is equal to (missing value) then
          error "Internal archive error in extracting"
        end if
      end if
      
      
if aUTI = "public.plain-text" then
        –Plain Text
        
set aStr to (NSString’s alloc()’s initWithData:aData encoding:(NSUTF8StringEncoding)) as string
        
      else if aUTI = "com.apple.applescript.script" then
        –AppleScript .scpt file
        
set aScript to (OSAScript’s alloc()’s initWithCompiledData:aData |error|:(missing value))
        
set aStr to (aScript’s source()) as string
        
      else if (my filterUTIList({aUTI}, "public.image")) is not equal to {} then
        –Various Images
        
set aStr to (NSImage’s alloc()’s initWithData:aData)
        
      end if
      
      
set the end of outList to aStr
    end if
  end repeat
  
  
return outList
end extractArchive

on retFileFormatUTI(aExt as string)
  tell script "BridgePlus"
    load framework
    
return (current application’s SMSForder’s UTIForExtension:aExt) as string
  end tell
end retFileFormatUTI

–UTIリストが指定UTIに含まれているかどうか演算を行う
on filterUTIList(aUTIList, aUTIstr)
  set anArray to NSArray’s arrayWithArray:aUTIList
  
set aPred to NSPredicate’s predicateWithFormat_("SELF UTI-CONFORMS-TO %@", aUTIstr)
  
set bRes to (anArray’s filteredArrayUsingPredicate:aPred) as list
  
return bRes
end filterUTIList

★Click Here to Open This Script 

Posted in file OSA Text URL UTI | Tagged 10.11savvy 10.12savvy 10.13savvy NSArray NSImage NSPredicate NSString NSURL NSURLTypeIdentifierKey NSUTF8StringEncoding OSAScript ZZArchive | Leave a comment

Safariで表示中のWebページの最終更新日時を取得

Posted on 8月 17, 2018 by Takaaki Naganoya

指定のWebページ(URL)の最終更新日時(Last Modified Date)を取得するAppleScriptです。

AppleScriptそのものにWebの最終更新日時を取得する関数や機能はありません。はい、おしまい。

……というのでは、AppleScriptの本質がぜんぜん分かっていないね、ということになります。AppleScriptは「それができるアプリケーション」(など)に依頼を出すのが処理スタイルだからです。

まずは、Safariにコマンドを投げて実行するスタイル。

AppleScript名:Safariのdo javascriptコマンドで最終更新日時を取得
— Created 2018-08-17 by Takaaki Naganoya
— 2018 Piyomaru Software
tell application "Safari"
  tell front document
    set dRes to (do JavaScript "document.lastModified;")
  end tell
end tell

★Click Here to Open This Script 

だいたいは、これで手を打つでしょう。ただし、最近のmacOSではセキュリティ強化のためにSafariのdo javascriptコマンドがデフォルトでは禁止されているので、Safariで「開発」メニューを表示させたあとに、「開発」メニューの「AppleEventからのJavaScriptを許可」「スマート検索フィールドからのJavaScriptを許可」を実行しておく必要があります(→ 書籍「AppleScript 10大最新技術」P-84)。

Mac AppStore上で配布/販売するアプリケーションの中で処理することを考えると、SafariをコントロールすることをInfo.plist内で宣言しておけばとくに問題はありません。

do javascriptコマンドの実行で一般的にはファイナルアンサーなのですが、なぜでしょう。リアルタイム日付が返ってくるパターンが多いです。

次は、shellのcurlコマンドを呼び出すスタイルです。指定URLのレスポンスヘッダーを出力させられるので、これを検索して出力します。ただ、YouTubeをはじめとするWebサイトでこの最終更新日を返してこないので、これでもダメな時はダメです。

AppleScript名:curlコマンドで最終更新日時を取得
— Created 2018-08-17 by Takaaki Naganoya
— 2018 Piyomaru Software
tell application "Safari"
  tell front document
    set aURL to URL
  end tell
end tell

try
  set uRes to (do shell script "curl -D – -s -o /dev/null " & aURL & " | grep Date:")
on error
  return false
end try

★Click Here to Open This Script 

これも現在日時を返してくるパターンが多いですね。また、噂レベルではあるものの「do shell scriptコマンドは極力使わないほうがいいよ」というお達しがScripter界隈で流れているので、将来的に何かがあるのかもしれません(昔の、ごくごく初期のMac OS XはBSDレイヤーというかBSDコマンド類が、OSインストール時にオプション扱いだったので、そういう未来はあるかもしれない)。

Mac AppStore上で配布/販売するアプリケーションの中で処理するのも、とくに問題はないのですが、今度はネットワーク接続することをあらかじめ宣言しておくのと、httpによる通信を行うことを宣言しておかないとネットワーク接続ができません。

最後の手段。Cocoaを呼び出して自前でWebのレスポンスヘッダーを取得するスタイル。

AppleScript名:Cocoaの機能で最終更新日時を取得
— Created 2018-08-17 by Takaaki Naganoya
— 2018 Piyomaru Software
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"

property NSString : a reference to current application’s NSString
property NSLocale : a reference to current application’s NSLocale
property NSURLRequest : a reference to current application’s NSURLRequest
property NSDateFormatter : a reference to current application’s NSDateFormatter
property NSURLConnection : a reference to current application’s NSURLConnection
property NSURLRequestUseProtocolCachePolicy : a reference to current application’s NSURLRequestUseProtocolCachePolicy

tell application "Safari"
  tell front document
    set aURL to URL
  end tell
end tell

set aURL to (current application’s |NSURL|’s URLWithString:aURL)
set {exRes, headerRes, aData} to checkURLResourceExistence(aURL, 3) of me
set aDate to headerRes’s |date| as string

set lastUpdateDate to dateFromStringWithDateFormat(aDate, "EEE, dd MMM yyyy HH:mm:ss zzz") of me
return lastUpdateDate

— 指定URLにファイル(画像など)が存在するかチェック
–> {存在確認結果(boolean), レスポンスヘッダー(NSDictionary), データ(NSData)}
on checkURLResourceExistence(aURL, timeOutSec as real)
  set aRequest to (NSURLRequest’s requestWithURL:aURL cachePolicy:(NSURLRequestUseProtocolCachePolicy) timeoutInterval:timeOutSec)
  
set aRes to (NSURLConnection’s sendSynchronousRequest:aRequest returningResponse:(reference) |error|:(missing value))
  
set dRes to (first item of (aRes as list))
  
set bRes to (second item of (aRes as list))
  
if bRes is not equal to missing value then
    set hRes to (bRes’s allHeaderFields())
    
set aResCode to (bRes’s statusCode()) as integer
  else
    set hRes to {}
    
set aResCode to -1 –error
  end if
  
return {(aResCode = 200), hRes, dRes}
end checkURLResourceExistence

–指定形式の日付テキストをAppleScriptのdateオブジェクトに変換
on dateFromStringWithDateFormat(dateString, dateFormat)
  set dStr to NSString’s stringWithString:dateString
  
set dateFormatStr to NSString’s stringWithString:dateFormat
  
  
set aDateFormatter to NSDateFormatter’s alloc()’s init()
  
aDateFormatter’s setDateFormat:dateFormatStr
  
aDateFormatter’s setLocale:(NSLocale’s alloc()’s initWithLocaleIdentifier:"en_US_POSIX")
  
  
set aDestDate to (aDateFormatter’s dateFromString:dStr)
  
  
return aDestDate as date
end dateFromStringWithDateFormat

★Click Here to Open This Script 

結果は3つとも変わりませんでした。Cocoa呼び出しするものも、作り置きしておいたサブルーチンを使いまわしただけなので、作るのに3分もかかっていません。

curlを呼び出すスタイル同様、Mac AppStore上で配布/販売するアプリケーションの中で処理するのもとくに問題はないのですが、httpによる通信を行うことを宣言しておかないとネットワーク接続ができません。

Safariでdo javascriptコマンドを実行するものは、最初にdo javascriptコマンドを実行する設定が必要。curlコマンドはまあそんなもんだろうかと。Cocoaの機能を呼び出す方法は、ここまでやってダメならあきらめがつくというところでしょうか。

Posted in Calendar Internet JavaScript URL | Tagged 10.11savvy 10.12savvy 10.13savvy NSDateFormatter NSLocale NSString NSURLConnection NSURLRequest Safari | Leave a comment

WordPressの指定IDの記事にリンクされているapplescriptからCocoa Classのproperty宣言を抽出 v3

Posted on 8月 5, 2018 by Takaaki Naganoya

本BlogのようなWordPressで運用されており、AppleScriptのURLリンクを記事に埋め込んでいるWordPressに対して、XML-RPC経由で指定IDの記事本文を取得し、埋め込まれているURLリンクからAppleScriptのソースコードを取得して、メモリー上でコンパイルして書式つきテキストに変換し、AppleScript構文書式をもとにpropertyラベルを抽出、そのうちCocoa Classのみをリストで取得するAppleScriptです。

本Blogに投稿した記事から宣言しているCocoa Classを抽出し、自動でタグ付けするために準備したものです。1記事に対して複数のAppleScriptが掲載されている場合にも対応しています。

HTMLReader.frameworkを用いてBlog本文からのリンク抽出、リンクURL抽出を行っています。

–> HTMLReader.framework(To ~/Library/Frameworks/)

本Sample Scriptで指定したIDの記事のproperty部分はこのようになっており、

本Scriptの実行によって抽出されたCocoa Class(単なる変数的なproperty項目や、Enumは対象外のため排除)は、

–> {“NSString”, “NSArray”, “OSAScript”, “NSPredicate”, “OSALanguage”, “NSDictionary”, “OSALanguageInstance”, “NSBundle”, “NSUnarchiver”}

のようになります。自分の環境でMacBook Proを有線ネットワーク接続した状態で、3.3〜3.4秒程度かかっています。大量の記事を処理する場合には本AppleScriptの並列処理を行うと処理時間の大幅な短縮が期待できます(MacのCPUがサーマルスロットリングで速度低下しなければ)。

また、負荷が集中している特定コアの動作周波数を上げ、他のコアの動作周波数を落とすTurbo Boostが有効な状態で並列処理を実行すると、並列処理を行う意義そのものが低下してしまうため、Turbo-Boost-Switcherのようなツールの併用が必要と思われます。

AppleScript名:WordPressの指定IDの記事にリンクされているapplescriptからCocoa Classのproperty宣言を抽出 v3
— Created 2018-07-30 by Takaaki Naganoya
— Modified 2018-08-05 by Takaaki Naganoya
— 2018 Piyomaru Software
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"
use framework "AppKit"
use framework "OSAKit"
use framework "HTMLReader" –https://github.com/nolanw/HTMLReader

property |NSURL| : a reference to current application’s |NSURL|
property NSArray : a reference to current application’s NSArray
property NSString : a reference to current application’s NSString
property NSBundle : a reference to current application’s NSBundle
property NSThread : a reference to current application’s NSThread
property OSAScript : a reference to current application’s OSAScript
property NSPredicate : a reference to current application’s NSPredicate
property NSTextView : a reference to current application’s NSTextView
property NSDictionary : a reference to current application’s NSDictionary
property NSUnarchiver : a reference to current application’s NSUnarchiver
property OSALanguage : a reference to current application’s OSALanguage
property OSAScriptView : a reference to current application’s OSAScriptView
property NSMutableArray : a reference to current application’s NSMutableArray
property OSAScriptController : a reference to current application’s OSAScriptController
property NSMutableDictionary : a reference to current application’s NSMutableDictionary
property OSALanguageInstance : a reference to current application’s OSALanguageInstance
property NSUTF8StringEncoding : a reference to current application’s NSUTF8StringEncoding

set postID to 3864
set {myUser, myPass} to getAcountData() of me
set aURL to "http://piyocast.com/as/xmlrpc.php"
set cocoaClassList to getCocoaPropListFromPost(aURL, postID, myUser, myPass) of me
–>  {"NSString", "NSBundle", "NSPredicate", "NSDictionary", "NSMutableArray", "NSMutableDictionary"}

–指定Blogの指定IDの記事にURLリンクされているAppleScriptから、Cocoa Classのpropertyのみ取得する
on getCocoaPropListFromPost(aURL, postID, myUser, myPass)
  –AppleScriptの構文色分け設定ファイルを読み込んで、重複色のチェックを実施  
  
set cList to getAppleScriptSourceColors() of me
  
set cRes to chkASLexicalFormatColorConfliction(cList) of me –構文色分けの重複色チェック
  
if cRes = false then error "There is some duplicate(s) color among AppleScript’s lexical color settings"
  
  
–WordPressの指定Post IDの記事を取得してリンクされているURLからURL Schemeでフィルタして、リンクされているAppleScriptのソースを取得
  
set aScheme to "applescript://"
  
set sourceList to getASSouceLinkedInWordPressPost(postID, aURL, aScheme, myUser, myPass) of me
  
  
–AppleScriptのソースをRAM上でコンパイル(構文確認)して、構文色分けしたRTFを取得。RTFの書式情報をparseしてattribute runsと同様のrecordを生成
  
–構文色分けをもとにproperty項目を抽出し、Cocoa Classに該当するもののみを抽出
  
set outList to {}
  
repeat with i in sourceList
    set j to contents of i
    
set anAttrStr to compileASandReturnAttributedString(j) of me
    
set attrRes to getAttributeRunsFromAttrString(anAttrStr) of me
    
set propNames to getPropertyNamesCocoaOnly(cList, attrRes) of me
    
    
if propNames is not equal to {} then
      set outList to outList & propNames
    end if
  end repeat
  
  
–1D Listのユニーク化(重複要素の排除)
  
set aArray to NSArray’s arrayWithArray:outList
  
set bArray to aArray’s valueForKeyPath:"@distinctUnionOfObjects.self"
  
set bList to bArray as list of string or string –as anything
  
  
return bList
end getCocoaPropListFromPost

–Property名称を取得する
on getPropertyNamesCocoaOnly(cList, aRec)
  script spdHnd
    property aRec : {}
  end script
  
  
set (aRec of spdHnd) to aRec
  
  
set targAttr to contents of item 7 of cList –ハンドラあるいは変数
  
set tmpCoStr to ((redValue of targAttr) as string) & " " & ((greenValue of targAttr) as string) & " " & ((blueValue of targAttr) as string)
  
  
set ontoColItem to contents of item 3 of cList –スクリプティング予約語(on/to)
  
set ontoCoStr to ((redValue of ontoColItem) as string) & " " & ((greenValue of ontoColItem) as string) & " " & ((blueValue of ontoColItem) as string)
  
  
  
–変数あるいはハンドラ名称をリストアップ(variables & handler)
  
set tmp1Array to NSArray’s arrayWithArray:(aRec of spdHnd)
  
set thePred0 to NSPredicate’s predicateWithFormat_("colorStr == %@", tmpCoStr)
  
set dArray to (tmp1Array’s filteredArrayUsingPredicate:thePred0) as list
  
  
–改行を含むデータをリストアップ(text data contains return)
  
set thePred1 to NSPredicate’s predicateWithFormat_("stringVal CONTAINS %@", return)
  
set eArray to ((tmp1Array’s filteredArrayUsingPredicate:thePred1)’s valueForKeyPath:"itemIndex") as list
  
  
set the beginning of eArray to 0 –ハンドラ宣言部がTopに来る場合に備える
  
  
–"property"(プロパティ宣言)の項目をリストアップ 文字と色で抽出
  
set thePred2 to NSPredicate’s predicateWithFormat_("stringVal == %@ && colorStr == %@ ", "property", ontoCoStr)
  
set fArray to ((tmp1Array’s filteredArrayUsingPredicate:thePred2)’s valueForKeyPath:"itemIndex") as list
  
  
set handlerList to {}
  
  
–property ではじまるハンドラの抽出
  
repeat with i in eArray –改行を含むテキストのアイテム番号リスト
    set j to (contents of i) as integer
    
repeat with ii in fArray –"on"の項目リスト
      set jj to (contents of ii) as integer
      
      
set handlerStr to missing value
      
      
if (j + 1) = jj then
        set handlerStr to stringVal of (item (jj + 2) of ((aRec of spdHnd) as list))
      else if (j + 2) = jj then
        set handlerStr to stringVal of (item (jj + 2) of ((aRec of spdHnd) as list))
      end if
      
      
set tmpStr to repChar(handlerStr, "|", "") of me
      
      
if tmpStr is not in {"error", missing value} and tmpStr is not in handlerList then
        –抽出したProperty宣言がCocoa Classのものかどうか判定
        
if searchClassInFrameworks(tmpStr) of me is not equal to false then
          set the end of handlerList to tmpStr
        end if
      end if
      
    end repeat
  end repeat
  
  
return handlerList
end getPropertyNamesCocoaOnly

–RAM上にスクリプトエディタと同じ部品を組み立て(非表示)、AppleScriptのソーステキストからObjectを生成し、Attributed Stringデータを返す
on compileASandReturnAttributedString(theSource as string)
  set targX to 1024 –View Width
  
set targY to 2048 –View Height
  
  
set osaCon to current application’s OSAScriptController’s alloc()’s init()
  
set osaView to current application’s OSAScriptView’s alloc()’s initWithFrame:(current application’s NSMakeRect(0, 0, targX, targY))
  
  
set resView to NSTextView’s alloc()’s initWithFrame:(current application’s NSMakeRect(0, 0, targX, targY))
  
resView’s setRichText:true
  
resView’s useAllLigatures:true
  
  
osaCon’s setScriptView:osaView
  
osaCon’s setLanguage:(OSALanguage’s languageForName:"AppleScript")
  
osaCon’s setResultView:resView
  
  
osaView’s setString:theSource
  
osaCon’s compileScript:(missing value) –Compile(構文確認)
  
  
set aRes to (osaView’s attributedString())
  
  
return aRes
end compileASandReturnAttributedString

–Attributed StringをDictionary化
on getAttributeRunsFromAttrString(theStyledText)
  script aSpd
    property styleList : {}
  end script
  
  
set (styleList of aSpd) to {} —for output
  
  
set thePureString to theStyledText’s |string|() –pure string from theStyledText
  
  
set theLength to theStyledText’s |length|()
  
set startIndex to 0
  
set itemCount to 1
  
  
repeat until (startIndex = theLength)
    set {theAtts, theRange} to theStyledText’s attributesAtIndex:startIndex longestEffectiveRange:(reference) inRange:{startIndex, theLength – startIndex}
    
    
–String  
    
set aText to (thePureString’s substringWithRange:theRange) as string
    
    
–Color
    
set aColor to (theAtts’s valueForKeyPath:"NSColor")
    
if aColor is not equal to missing value then
      set aSpace to aColor’s colorSpace()
      
      
set aRed to (aColor’s redComponent()) * 255
      
set aGreen to (aColor’s greenComponent()) * 255
      
set aBlue to (aColor’s blueComponent()) * 255
      
      
set colList to {aRed as integer, aGreen as integer, aBlue as integer} –for comparison
      
set colStrForFind to (aRed as integer as string) & " " & (aGreen as integer as string) & " " & (aBlue as integer as string) –for filtering
    else
      set colList to {0, 0, 0}
      
set colStrForFind to "0 0 0"
    end if
    
    
–Font
    
set aFont to (theAtts’s valueForKeyPath:"NSFont")
    
if aFont is not equal to missing value then
      set aDFontName to aFont’s displayName()
      
set aDFontSize to aFont’s pointSize()
    end if
    
    
set the end of (styleList of aSpd) to {stringVal:aText, colorStr:colStrForFind, colorVal:colList, fontName:aDFontName as string, fontSize:aDFontSize, itemIndex:itemCount}
    
set startIndex to current application’s NSMaxRange(theRange)
    
    
set itemCount to itemCount + 1
  end repeat
  
  
return (styleList of aSpd)
  
end getAttributeRunsFromAttrString

–指定クラスがいずれかのCocoa Frameworkに所属しているかを検索
on searchClassInFrameworks(aTarget)
  set aClass to current application’s NSClassFromString(aTarget)
  
if aClass = missing value then return false
  
set theComponenents to (NSBundle’s bundleForClass:aClass)’s bundleURL’s pathComponents()
  
set thePred to NSPredicate’s predicateWithFormat:"pathExtension == ’framework’"
  
set aRes to (theComponenents’s filteredArrayUsingPredicate:thePred)’s firstObject() as list of string or string
  
return aRes
end searchClassInFrameworks

–指定Post IDのWordPress記事から、指定SchemeのURLを抽出し、AS Sourceをdecodeしてproperty行のみ抽出
on getASSouceLinkedInWordPressPost(postID, aURL, aScheme, myUser, myPass)
  –call xmlrpc命令に対するURLの間接指定を有効にするために、AppleScriptの構文解釈機能をダミーURLでだます
  
using terms from application "http://piyocast.com/as/xmlrpc.php" –URLと判定されればなんでもいい
    tell application aURL
      set wRes to (call xmlrpc {method name:"wp.getPost", parameters:{"1", myUser, myPass, postID as string}})
    end tell
  end using terms from
  
  
set aBody to post_content of wRes –Blog本文
  
  
–記事中でリンクしているURLを取得し、指定のURL Schemeでフィルタする
  
set urlList to filterURLLinksByScheme(aBody, aScheme) of me
  
  
set propList to {}
  
  
repeat with i in urlList
    set j to contents of i
    
set urlRec to parseQueryDictFromURLString(j) of me
    
set tmpScript to (urlRec’s |script|) as string –Get AppleScript Source
    
    
set propList to propList & tmpScript
  end repeat
  
  
return propList
end getASSouceLinkedInWordPressPost

on parseQueryDictFromURLString(aURLStr as string)
  if aURLStr = "" then error "No URL String"
  
  
set aURL to |NSURL|’s URLWithString:aURLStr
  
set aQuery to aURL’s query() –Get Query string part from URL
  
if aQuery’s |length|() = 0 then return false
  
  
set aDict to NSMutableDictionary’s alloc()’s init()
  
set aParamList to (aQuery’s componentsSeparatedByString:"&") as list
  
  
repeat with i in aParamList
    set j to contents of i
    
if length of j > 0 then
      set tmpStr to (NSString’s stringWithString:j)
      
set eList to (tmpStr’s componentsSeparatedByString:"=")
      
set anElement to (eList’s firstObject()’s stringByReplacingPercentEscapesUsingEncoding:(NSUTF8StringEncoding))
      
set aValStr to (eList’s lastObject()’s stringByReplacingPercentEscapesUsingEncoding:(NSUTF8StringEncoding))
      (
aDict’s setObject:aValStr forKey:anElement)
    end if
  end repeat
  
  
return aDict
end parseQueryDictFromURLString

–指定のHTML文字列から、Link URLを抽出し、schemeで再抽出する
on filterURLLinksByScheme(aBody, aScheme)
  set conType to "text/html"
  
  
–HTML文字列をいったんNSDataにしているのは、HTMLReader.frameworkの仕様のため
  
set aData to (current application’s NSString’s stringWithString:aBody)’s dataUsingEncoding:(current application’s NSUTF8StringEncoding)
  
set aHTML to current application’s HTMLDocument’s documentWithData:aData contentTypeHeader:conType
  
  
set aTextArray to ((aHTML’s nodesMatchingSelector:"a")’s textContent) as list –リンク文字
  
set aLinkList to ((aHTML’s nodesMatchingSelector:"a")’s attributes’s valueForKeyPath:"href") as list –URL文字列
  
  
set outList to {}
  
repeat with i in aLinkList
    set j to contents of i
    
if j begins with aScheme then
      set the end of outList to j
    end if
  end repeat
  
  
return outList
end filterURLLinksByScheme

–文字置換
on repChar(origText as string, targChar as string, repChar as string)
  set curDelim to AppleScript’s text item delimiters
  
set AppleScript’s text item delimiters to targChar
  
set tmpList to text items of origText
  
set AppleScript’s text item delimiters to repChar
  
set retText to tmpList as string
  
set AppleScript’s text item delimiters to curDelim
  
return retText
end repChar

–AppleScriptの構文色分けのカラー値をRGBで取得する
on getAppleScriptSourceColors()
  — get the plist info as a dictionary
  
set thePath to NSString’s stringWithString:"~/Library/Preferences/com.apple.applescript.plist"
  
set thePath to thePath’s stringByExpandingTildeInPath()
  
set theInfo to NSDictionary’s dictionaryWithContentsOfFile:thePath
  
  
— extract relevant part and loop through
  
set theArray to (theInfo’s valueForKey:"AppleScriptSourceAttributes") as list
  
  
set colList to {}
  
  
repeat with i from 1 to count of theArray
    set anEntry to item i of theArray
    
    
set colorData to NSColor of anEntry
    
set theColor to (NSUnarchiver’s unarchiveObjectWithData:colorData)
    
    
set {rVal, gVal, bVal} to retColListFromNSColor(theColor, 255) of me
    
    
set fontData to NSFont of anEntry
    
set theFont to (NSUnarchiver’s unarchiveObjectWithData:fontData)
    
    
set aFontName to theFont’s displayName() as text
    
set aFontSize to theFont’s pointSize()
    
    
set aColRec to {redValue:rVal, greenValue:gVal, blueValue:bVal, fontName:aFontName, fontSize:aFontSize}
    
    
set the end of colList to aColRec
  end repeat
  
  
return colList
end getAppleScriptSourceColors

–NSColorからRGBの値を取り出す
on retColListFromNSColor(aCol, aMAX as integer)
  set aRed to round ((aCol’s redComponent()) * aMAX) rounding as taught in school
  
set aGreen to round ((aCol’s greenComponent()) * aMAX) rounding as taught in school
  
set aBlue to round ((aCol’s blueComponent()) * aMAX) rounding as taught in school
  
  
if aRed > aMAX then set aRed to aMAX
  
if aGreen > aMAX then set aGreen to aMAX
  
if aBlue > aMAX then set aBlue to aMAX
  
  
return {aRed, aGreen, aBlue}
end retColListFromNSColor

–AS書式で配色に重複がないかどうかチェック
on chkASLexicalFormatColorConfliction(aList)
  set anArray to current application’s NSArray’s arrayWithArray:aList
  
set bList to (anArray’s valueForKeyPath:"redValue.stringValue") as list
  
set cList to (anArray’s valueForKeyPath:"greenValue.stringValue") as list
  
set dList to (anArray’s valueForKeyPath:"blueValue.stringValue") as list
  
  
set colStrList to {}
  
repeat with i from 1 to (length of bList)
    set bItem to contents of item i of bList
    
set cItem to contents of item i of cList
    
set dItem to contents of item i of dList
    
set the end of colStrList to bItem & " " & cItem & " " & dItem
  end repeat
  
  
set aRes to returnDuplicatesOnly(colStrList) of me
  
if aRes is equal to {} then
    return true –重複が存在しなかった場合
  else
    return false –重複があった場合
  end if
end chkASLexicalFormatColorConfliction

on returnDuplicatesOnly(aList as list)
  set aSet to current application’s NSCountedSet’s alloc()’s initWithArray:aList
  
set bList to (aSet’s allObjects()) as list
  
  
set dupList to {}
  
repeat with i in bList
    set aRes to (aSet’s countForObject:i)
    
if aRes > 1 then
      set the end of dupList to (contents of i)
    end if
  end repeat
  
  
return dupList
end returnDuplicatesOnly

on getAcountData()
  return {"xxxxxxxx_xx", "XXXXXXXXXXXXXXXXXXXXXXXX"} –user name, password
end getAcountData

★Click Here to Open This Script 

Posted in list OSA RTF Text URL XML-RPC | Tagged 10.11savvy 10.12savvy 10.13savvy NSArray NSBundle NSDictionary NSMutableArray NSMutableDictionary NSPredicate NSString NSTextView NSThread NSUnarchiver OSALanguage OSALanguageInstance OSAScript OSAScriptController OSAScriptView | Leave a comment

WordPressの指定IDの記事にリンクされているapplescriptからCocoa Classのproperty宣言を抽出

Posted on 7月 30, 2018 by Takaaki Naganoya

WordPressで稼働しているBlog(AppleScriptの穴)の、指定記事IDの本文を取得し、本文内でリンクしているURLのうち指定schemeのものを抽出し、URLリンクされているAppleScriptのソースをデコードしてproperty宣言文のうちCocoa Classの宣言を行っているものを抽出するAppleScriptです。

HTMLReader.frameworkを用いてBlog本文からのリンク抽出、リンクURL抽出を行っています。

–> HTMLReader.framework(To ~/Library/Frameworks/)

本Blogで、Tagの運用を変更しようと思い立ち、手作業で修正をはじめました。アプリケーション名のほかにCocoa Class名をTagにしようという試みです。

ただ、数個の記事のTag付け直しを行っただけで「手作業では終わらない」ことが判明。2月に再構築をはじめて1,000本ぐらいの記事をアップしているので、手作業ではとても無理です

本Blogの記事にURLリンクされているAppleScriptソースプログラムを(XML-RPC経由で)AppleScriptから自動で取得し、

その中のproperty宣言文を抽出して、

Cocoa Classの宣言文のみをリストアップして、

Blog記事のTagに自動で指定できないか、と試してみたものです。

現時点で、

  (1)指定Blog記事の本文を取得
  (2)(1)から指定URL SchemeのリンクURLを抽出
  (3)(2)のURL EncodeされているAppleScriptソースをDecode
  (4)property宣言文のみ抽出
  (5)property宣言ラベルがCocoa Classのものかをチェックして抽出

というところまでできています。本プログラムは、BlogのUser NameとPasswordが必要なので、リストのまま動かしてもエラーになり動作しません。同様にWordPressで運用されているBlogがあれば、そちらで試してみるのもよいでしょう。

XML-RPCでWordPressと通信するのには、記事アップロード自動化に使ったFrameworkもありますが、ためしにAppleScript標準搭載のcall xmlrpcコマンドを使ってみました。記事新規投稿コマンドだとクラッシュを起こしますが、この程度の用途ならクラッシュせずに使えるようです。

また、property文の抽出は構文要素を考慮していないため、コメントアウトされているものも拾ってくる可能性があります(単に行頭のproperty宣言文を拾っているだけなので、複数行コメントになっているものは拾われてくることでしょう)。

(*
property NSArray: a reference to current application’s NSArray
property NSString: a reference to current application’s NSString
*)

これを防ぐために、URLリンクされたAppleScriptをデコードした後で、いったんAppleScriptとして構文確認(コンパイル)を実施して、実際のAppleScriptとして評価すべきなのでしょう。

AppleScript名:WordPressの指定IDの記事にリンクされているapplescriptからCocoa Classのproperty宣言を抽出 v2
— Created 2018-07-30 by Takaaki Naganoya
— Modified 2018-07-31 by Takaaki Naganoya
— 2018 Piyomaru Software
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"
use framework "AppKit"
use framework "HTMLReader" –https://github.com/nolanw/HTMLReader

property |NSURL| : a reference to current application’s |NSURL|
property NSString : a reference to current application’s NSString
property NSBundle : a reference to current application’s NSBundle
property NSPredicate : a reference to current application’s NSPredicate
property NSDictionary : a reference to current application’s NSDictionary
property NSMutableArray : a reference to current application’s NSMutableArray
property NSMutableDictionary : a reference to current application’s NSMutableDictionary
property NSUTF8StringEncoding : a reference to current application’s NSUTF8StringEncoding

–PostID
set postID to 3826
set aScheme to "applescript://"

–WordPressの指定Post IDの記事を取得してリンクされているURLからURL Schemeでフィルタして、リンクされているAppleScriptのソースを
–取得し、AS Sourceからproperty宣言文のみ抽出
set pList to getASSouceLinkedInWordPressPost(postID, aScheme) of me
–>  {"property NSBundle : a reference to current application’s NSBundle", "property |NSURL| : a reference to current application’s |NSURL|", "property HTMLDocument : a reference to current application’s HTMLDocument", "property NSMutableDictionary : a reference to current application’s NSMutableDictionary", "property NSPredicate : a reference to current application’s NSPredicate", "property NSUTF8StringEncoding : a reference to current application’s NSUTF8StringEncoding", "property NSMutableSet : a reference to current application’s NSMutableSet", "property NSRegularExpressionSearch : a reference to current application’s NSRegularExpressionSearch", "property NSString : a reference to current application’s NSString", "property NSSortDescriptor : a reference to current application’s NSSortDescriptor"}

–property宣言文リストから、propetyがCocoa Classの宣言であるものだけを抽出
set p2List to filterPropertySentenseWhetherCocoaOrNot(pList) of me
–>  {"NSBundle", "HTMLDocument", "NSMutableDictionary", "NSPredicate", "NSMutableSet", "NSString", "NSSortDescriptor"}

on filterPropertySentenseWhetherCocoaOrNot(pList)
  set outList to {}
  
  
repeat with i in pList
    set j to contents of i
    
set j2 to repChar(j, "|", "") of me
    
    
–Parse String Into Words by Space
    
set aTmpStr to (NSString’s stringWithString:j2)
    
set wList to (aTmpStr’s componentsSeparatedByString:" ") as list
    
    
if wList contains {"a", "reference", "to", "current", "application’s"} then
      –通常のクラス名の場合(クラス名以外のpropertyの場合もある)
      
set aTarg to contents of item 2 of wList
      
      
–property値がCocoa Classかどうかを判定    
      
set fRes to searchClassInFrameworks(aTarg) of me
      
      
if fRes is not equal to false then
        set the end of outList to aTarg
      end if
    end if
  end repeat
  
  
return outList
end filterPropertySentenseWhetherCocoaOrNot

–指定クラスがいずれかのCocoa Frameworkに所属しているかを検索
on searchClassInFrameworks(aTarget)
  set aClass to current application’s NSClassFromString(aTarget)
  
if aClass = missing value then return false
  
set theComponenents to (NSBundle’s bundleForClass:aClass)’s bundleURL’s pathComponents()
  
set thePred to NSPredicate’s predicateWithFormat:"pathExtension == ’framework’"
  
set aRes to (theComponenents’s filteredArrayUsingPredicate:thePred)’s firstObject() as list of string or string
  
return aRes
end searchClassInFrameworks

–指定Post IDのWordPress記事から、指定SchemeのURLを抽出し、AS Sourceをdecodeしてproperty行のみ抽出
on getASSouceLinkedInWordPressPost(postID, aScheme)
  set {myUser, myPass} to getAcountData() of me
  
  
tell application "http://piyocast.com/as/xmlrpc.php"
    set wRes to (call xmlrpc {method name:"wp.getPost", parameters:{"1", myUser, myPass, postID as string}})
  end tell
  
set aBody to post_content of wRes –Blog本文
  
  
–記事中でリンクしているURLを取得し、指定のURL Schemeでフィルタする
  
set urlList to filterURLLinksByScheme(aBody, aScheme) of me
  
  
set propList to {}
  
  
repeat with i in urlList
    set j to contents of i
    
set urlRec to parseQueryDictFromURLString(j) of me
    
set tmpScript to (urlRec’s |script|) as string –Get AppleScript Source
    
    
set tList to paragraphs of tmpScript
    
set pList to filterListUsingPredicate(tList, "SELF BEGINSWITH[c] %@", "property") –後方一致
    
    
set propList to propList & pList
  end repeat
  
  
return propList
end getASSouceLinkedInWordPressPost

on filterListUsingPredicate(aList as list, aPredicateStr as string, targStr as string)
  set setKey to current application’s NSMutableSet’s setWithArray:aList
  
set aPredicate to current application’s NSPredicate’s predicateWithFormat_(aPredicateStr, targStr)
  
set aRes to (setKey’s filteredSetUsingPredicate:aPredicate)
  
set bRes to aRes’s allObjects()
  
set cRes to bRes as {list, list of string or string}
  
return cRes
end filterListUsingPredicate

on parseQueryDictFromURLString(aURLStr as string)
  if aURLStr = "" then error "No URL String"
  
  
set aURL to |NSURL|’s URLWithString:aURLStr
  
set aQuery to aURL’s query() –Get Query string part from URL
  
if aQuery’s |length|() = 0 then return false
  
  
set aDict to NSMutableDictionary’s alloc()’s init()
  
set aParamList to (aQuery’s componentsSeparatedByString:"&") as list
  
  
repeat with i in aParamList
    set j to contents of i
    
if length of j > 0 then
      set tmpStr to (NSString’s stringWithString:j)
      
set eList to (tmpStr’s componentsSeparatedByString:"=")
      
set anElement to (eList’s firstObject()’s stringByReplacingPercentEscapesUsingEncoding:(NSUTF8StringEncoding))
      
set aValStr to (eList’s lastObject()’s stringByReplacingPercentEscapesUsingEncoding:(NSUTF8StringEncoding))
      (
aDict’s setObject:aValStr forKey:anElement)
    end if
  end repeat
  
  
return aDict
end parseQueryDictFromURLString

–指定のHTML文字列から、Link URLを抽出し、schemeで再抽出する
on filterURLLinksByScheme(aBody, aScheme)
  set conType to "text/html"
  
  
–HTML文字列をいったんNSDataにしているのは、HTMLReader.frameworkの仕様のため
  
set aData to (current application’s NSString’s stringWithString:aBody)’s dataUsingEncoding:(current application’s NSUTF8StringEncoding)
  
set aHTML to current application’s HTMLDocument’s documentWithData:aData contentTypeHeader:conType
  
  
set aTextArray to ((aHTML’s nodesMatchingSelector:"a")’s textContent) as list –リンク文字
  
set aLinkList to ((aHTML’s nodesMatchingSelector:"a")’s attributes’s valueForKeyPath:"href") as list –URL文字列
  
  
set outList to {}
  
repeat with i in aLinkList
    set j to contents of i
    
if j begins with aScheme then
      set the end of outList to j
    end if
  end repeat
  
  
return outList
end filterURLLinksByScheme

–文字置換
on repChar(origText as string, targChar as string, repChar as string)
  set curDelim to AppleScript’s text item delimiters
  
set AppleScript’s text item delimiters to targChar
  
set tmpList to text items of origText
  
set AppleScript’s text item delimiters to repChar
  
set retText to tmpList as string
  
set AppleScript’s text item delimiters to curDelim
  
return retText
end repChar

on getAcountData()
  return {"xxxxxxxx_xx", "XXXXXXXXXXXXXXXXXXXXXXXX"}
end getAcountData

★Click Here to Open This Script 

Posted in list Network Record URL XML-RPC | Tagged 10.11savvy 10.12savvy 10.13savvy NSArray NSBundle NSString NSURL | Leave a comment

note.muで指定のユーザーのノートを取得する

Posted on 7月 22, 2018 by Takaaki Naganoya

note.muの非公開REST API(でも、みんな知っている)を呼び出して、指定ユーザーのノート(投稿記事)を取得するAppleScriptです。

使い慣れたNSURLConnectionがmacOS 10.11で非推奨APIになって久しく、頻繁に使っているREST API呼び出し処理がいきなり動かなくなるのは困るため、NSURLSessionを用いてREST APIを呼び出すよう徐々に変更しています。

当初、REST APIの呼び出し部分は共通ルーチン化して、さまざまなREST APIを共通ルーチンで処理できるとよいだろうかと思っていました。実際にさまざまなREST APIを呼び出してみると、APIごとに意外なほど癖があって、なかなか「すべてのREST APIの共通処理ルーチン」というところまで共通化する段階までには至っていないところ。

左がNSURLSessionを用いた呼び出し、右がNSURLConnectionを用いた呼び出しを行なったものです。せっかくNSURLSessionを使ってみてはいるものの、同一の処理でもNSURLConnectionの方が40%ぐらい高速です。同一ネットワーク環境(iPhone経由のテザリング接続)でNSURLConnectionが0.4秒ぐらい、NSURLSessionでだいたい0.5から0.7秒といったところ。有線ネットワーク接続だともう少し速度が改善するかもしれません。

NSURLSessionに明示的な同期処理が存在しないため、NSURLSessionが受けわたすdelegateイベントをAppleScript側でループしつつ待っており、NSURLConnectionで素直に同期処理メソッドを呼び出すよりもオーバーヘッドが大きくなっているものと推測しています。

NSURLSessionでREST APIを呼び出すあたりの処理をまるごと独立したFrameworkにでも追い出すとよいのではないか、といったところです。

AppleScript名:(GET)指定のユーザーのノート v2
— Created 2018-06-16 by Takaaki Naganoya
— 2018 Piyomaru Software
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"

property |NSURL| : a reference to current application’s |NSURL|
property NSString : a reference to current application’s NSString
property NSURLSession : a reference to current application’s NSURLSession
property NSJSONSerialization : a reference to current application’s NSJSONSerialization
property NSMutableURLRequest : a reference to current application’s NSMutableURLRequest
property NSUTF8StringEncoding : a reference to current application’s NSUTF8StringEncoding
property NSURLSessionConfiguration : a reference to current application’s NSURLSessionConfiguration

property retData : missing value

set retData to missing value

set reqURLStr to "https://note.mu/api/v1/notes"
set aRec to {urlname:"140software"} –note ID
set aURL to retURLwithParams(reqURLStr, aRec) of me

set aRESTres to callRestGETAPIAndParseResults(aURL) of me

on callRestGETAPIAndParseResults(reqURLStr)
  set aURL to |NSURL|’s URLWithString:reqURLStr
  
set aRequest to NSMutableURLRequest’s requestWithURL:aURL
  
aRequest’s setHTTPMethod:"GET"
  
aRequest’s setTimeoutInterval:10
  
aRequest’s setValue:"gzip" forHTTPHeaderField:"Content-Encoding"
  
aRequest’s setValue:"application/json; charset=UTF-8" forHTTPHeaderField:"Content-Type"
  
  
set aConfig to NSURLSessionConfiguration’s defaultSessionConfiguration()
  
set aSession to NSURLSession’s sessionWithConfiguration:aConfig delegate:(me) delegateQueue:(missing value)
  
set aTask to aSession’s dataTaskWithRequest:aRequest
  
  
aTask’s resume() –Start URL Session
  
  
repeat 10000 times
    if retData is not equal to missing value then exit repeat
    
current application’s NSThread’s sleepForTimeInterval:("0.001" as real) –delay 0.001
  end repeat
  
  
retData
end callRestGETAPIAndParseResults

on URLSession:tmpSession dataTask:tmpTask didReceiveData:tmpData
  set aStat to (tmpTask’s state()) as list of string or string
  
  
set resStr to NSString’s alloc()’s initWithData:tmpData encoding:(NSUTF8StringEncoding)
  
set jsonString to NSString’s stringWithString:(resStr)
  
  
set jsonData to jsonString’s dataUsingEncoding:(NSUTF8StringEncoding)
  
set aJsonDict to NSJSONSerialization’s JSONObjectWithData:jsonData options:0 |error|:(missing value)
  
  
set retData to aJsonDict as list of string or string
end URLSession:dataTask:didReceiveData:

on retURLwithParams(aBaseURL, aRec)
  set aDic to current application’s NSMutableDictionary’s dictionaryWithDictionary:aRec
  
set aKeyList to (aDic’s allKeys()) as list
  
set aValList to (aDic’s allValues()) as list
  
set aLen to length of aKeyList
  
  
set qList to {}
  
repeat with i from 1 to aLen
    set aName to contents of item i of aKeyList
    
set aVal to contents of item i of aValList
    
set the end of qList to (current application’s NSURLQueryItem’s queryItemWithName:aName value:aVal)
  end repeat
  
  
set aComp to current application’s NSURLComponents’s alloc()’s initWithString:aBaseURL
  
aComp’s setQueryItems:qList
  
set aURL to (aComp’s |URL|()’s absoluteString()) as text
  
  
return aURL
end retURLwithParams

★Click Here to Open This Script 

Posted in JSON list REST API URL | Tagged 10.11savvy 10.12savvy 10.13savvy NSJSONSerialization NSMutableURLRequest NSString NSURL NSURLSession NSURLSessionConfiguration NSUTF8StringEncoding | 1 Comment

Recruit Tech Japanese Text Summarization API

Posted on 6月 28, 2018 by Takaaki Naganoya

Recruit techのA3RT機械学習ソリューションAPIのひとつ、「Text Summarization API」(日本語文章要約API)を呼び出すAppleScriptです。

APIの説明ページからAPI Keyを取得して本リストに記載して実行させてみてください。

他の2つのAPIが箸にも棒にもひっかからないレベルであるのとは異なり、本APIは割と使えます。

使えるというよりも、「日本語文章要約」という処理の結果が適切かどうか、その妥当性の評価が難しいので「とりあえずそれっぽく動いている」ように見えます。

論文やニュース記事のような要約に適した論理構造の文章というものが、一般的にはかなり「まれ」な存在であり、たいていは要約してみても納得しづらいものになりがちです。とくに論理ではなく情緒に訴えるような「くだけた」書き方をすると要約しづらいものになります。

AppleScript自体にも「summarize」という文章要約コマンドが標準装備されており、日本語の文章に対しても実行できますが、処理結果に100%納得できているわけではありません。半信半疑というところです。

本来、文章要約という処理を考えると、「主語」が何か、その主語がどのようなアクションを起こしたり、起こされたりしたのか、という処理を期待したいところですが、かならずしもそのような処理結果が得られるわけではありません。文章構造上、重要と思われるような箇所をそのまま抜き出すような処理が行われます。

そのため、大当たりという印象はないものの、大外しになることもなく「なんとなくそんな感じ?」という割り切れないものを感じてしまいがちです。

日本語文章の要約はいつの日か実用的な、納得できるレベルの処理ができるようになるのかもしれませんが、目下のところはこのレベルで満足するべきなのかもしれません。

AppleScript名:Recruit Tech Japanese Text Summarization API
— Created 2018-06-13 by Takaaki Naganoya
— 2018 Piyomaru Software
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"
–https://a3rt.recruit-tech.co.jp/product/TextSummarizationAPI/

set apiKeyStr to getAPIKey() of me
set targText to returnBody() of me

set sText to "curl -X POST -d ’apikey=" & apiKeyStr & "’ –data-urlencode ’sentences=" & targText & "’ ’https://api.a3rt.recruit-tech.co.jp/text_summarization/v1/’"
set aRes to do shell script sText
set jsonString to current application’s NSString’s stringWithString:aRes
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 aRec to aJsonDict as record
–>  {​​​​​message:"Summarization is completed", ​​​​​status:0, ​​​​​summary:{​​​​​​​"世間的には「Appleが新製品を発表するイベント」と見られているけれども、本来そんなイベントではありません"​​​​​}​​​}

on getAPIKey()
  return "xXXXXXXxXxxxXXxxXxXXXxXxXXxxxxxx"
end getAPIKey

on returnBody()
  return "WWDCというイベントがあります。World Wide Developpers Conference。Appleが1年に一度、開発者を集めて「これからこういう感じのOSにしまっせー」という発表を行う場です。WWDCは例年、カリフォルニア州のモスコーニュ・センターというバカでかいイベント会場で行われます。数千人単位で収容できる会場で、参加者は朝の4~5時ぐらいから並ぶと聞きました。遅く行くと後ろの方のスクリーンから遠い席になるのだとか。世間的には「Appleが新製品を発表するイベント」と見られているけれども、本来そんなイベントではありません。ほかにいろいろイベントがあったのが、WWDCだけ残ったのでたまたま新製品発表があったりするだけの話で、なにが言いたいかといえば、本来はハードウェア製品の発表の場ではなくてOSなどのソフトウェアの(開発者向けの)話をする発表会でした。"
end returnBody

★Click Here to Open This Script 

AppleScript名:summarize
set aStr to returnBody() of me
set bStr to summarize aStr in 1
–> "ほかにいろいろイベントがあったのが、WWDCだけ残ったのでたまたま新製品発表があったりするだけの話で、なにが言いたいかといえば、本来はハードウェア製品の発表の場ではなくてOSなどのソフトウェアの(開発者向けの)話をする発表会でした。"

on returnBody()
  return "WWDCというイベントがあります。World Wide Developpers Conference。Appleが1年に一度、開発者を集めて「これからこういう感じのOSにしまっせー」という発表を行う場です。WWDCは例年、カリフォルニア州のモスコーニュ・センターというバカでかいイベント会場で行われます。数千人単位で収容できる会場で、参加者は朝の4~5時ぐらいから並ぶと聞きました。遅く行くと後ろの方のスクリーンから遠い席になるのだとか。世間的には「Appleが新製品を発表するイベント」と見られているけれども、本来そんなイベントではありません。ほかにいろいろイベントがあったのが、WWDCだけ残ったのでたまたま新製品発表があったりするだけの話で、なにが言いたいかといえば、本来はハードウェア製品の発表の場ではなくてOSなどのソフトウェアの(開発者向けの)話をする発表会でした。"
end returnBody

★Click Here to Open This Script 

Posted in REST API Text URL | Tagged 10.11savvy 10.12savvy 10.13savvy | Leave a comment

Call Postmark’s spam detection API

Posted on 6月 21, 2018 by Takaaki Naganoya

PostmarkのSpamAssassin REST APIを呼び出してメールのスパム判定を行うAppleScriptです。

NSURLSessionを用いてPOST methodのREST APIを呼び出しています。

メール本文のソーステキストを渡すと、スパム度のスコアを計算して返してくれます。パラメータの「options」で「short」か「long」を指定でき、最終的な評価値のみ知りたい場合には前者を、詳細情報を知りたい場合には後者を指定することになります。

すでにMail.appにスパム判定の機能が標準搭載されているため、スパムフィルタ単体で利用する用途というのは減ってきましたが、メールの送信前にスパムフィルタに引っかからないかをチェックしておく(メールマガジン作成時など)ためには「あったほうが便利」な処理です。

本APIを利用するのに、事前のユーザー登録やAPI Keyを取得する必要はありません。このリストを実行するとそのまま結果が得られます。Mail.appのメッセージのソースを渡すのもたいして手間はかかりません。

ただし、本APIは継続して提供される保証もありませんし、トラブルが発生して動作が止まっていた場合に対処してくれたりはしません。実際の業務ほかシビアな用途で利用するのはためらわれるものがあります。

ローカルにSpamSieveをインストールしてAppleScriptで呼び出すと同様にスパム評価値を計算してくれるので、メールのSPAM判定のための用途にはこれも検討に値するでしょう。

AppleScript名:Call Postmark’s spam API
— Created 2018-06-20 by Takaaki Naganoya
— 2018 Piyomaru Software
use AppleScript version "2.5"
use scripting additions
use framework "Foundation"

property |NSURL| : a reference to current application’s |NSURL|
property NSString : a reference to current application’s NSString
property NSURLSession : a reference to current application’s NSURLSession
property NSJSONSerialization : a reference to current application’s NSJSONSerialization
property NSMutableURLRequest : a reference to current application’s NSMutableURLRequest
property NSUTF8StringEncoding : a reference to current application’s NSUTF8StringEncoding
property NSURLSessionConfiguration : a reference to current application’s NSURLSessionConfiguration

property retData : missing value
property retCode : 0
property retHeaders : 0

set aBody to "raw dump of email"

–https://spamcheck.postmarkapp.com/doc/
set aURL to "https://spamcheck.postmarkapp.com/filter"
set aRec to {email:aBody, options:"long"}

set aRESTres to callRestPOSTAPIAndParseResults(aURL, aRec, 10) of me
set aRESTcode to retCode of aRESTres
–> 200

set aRESTheader to retHeaders of aRESTres
set aRESTres to retData of aRESTres
–> {success:true, rules:{{score:"1.2", |description|:"Missing To: header"}, {score:"-0.0", |description|:"Informational: message was not relayed via SMTP"}, {score:"1.4", |description|:"Missing Date: header"}, {score:"1.8", |description|:"Missing Subject: header"}, {score:"2.3", |description|:"Message appears to have no textual parts and no Subject: text"}, {score:"0.1", |description|:"Missing Message-Id: header"}, {score:"-0.0", |description|:"Informational: message has no Received headers"}, {score:"1.0", |description|:"Missing From: header"}, {score:"0.0", |description|:"Message appears to be missing most RFC-822 headers"}}, score:"7.9", report:" …."}

on callRestPOSTAPIAndParseResults(reqURLStr as string, aRec as record, timeoutSec as integer)
  set retData to missing value
  
set retCode to 0
  
set retHeaders to {}
  
  
set aURL to |NSURL|’s URLWithString:reqURLStr
  
set aRequest to NSMutableURLRequest’s requestWithURL:aURL
  
aRequest’s setHTTPMethod:"POST"
  
aRequest’s setCachePolicy:(current application’s NSURLRequestReloadIgnoringLocalCacheData)
  
aRequest’s setHTTPShouldHandleCookies:false
  
aRequest’s setTimeoutInterval:timeoutSec
  
–aRequest’s setValue:"gzip" forHTTPHeaderField:"Content-Encoding"–Does not work with this API & Method
  
aRequest’s setValue:"application/json" forHTTPHeaderField:"Accept"
  
aRequest’s setValue:"application/json; charset=UTF-8" forHTTPHeaderField:"Content-Type"
  
  
set dataJson to current application’s NSJSONSerialization’s dataWithJSONObject:aRec options:0 |error|:(missing value)
  
aRequest’s setHTTPBody:dataJson
  
  
set aConfig to NSURLSessionConfiguration’s defaultSessionConfiguration()
  
set aSession to NSURLSession’s sessionWithConfiguration:aConfig delegate:(me) delegateQueue:(missing value)
  
set aTask to aSession’s dataTaskWithRequest:aRequest
  
  
set hitF to false
  
aTask’s resume() –Start URL Session
  
  
repeat (1000 * timeoutSec) times
    if retData is not equal to missing value then
      set hitF to true
      
exit repeat
    end if
    
delay ("0.001" as real)
  end repeat
  
  
if hitF = false then error "REST API Timeout Error"
  
  
return {retData:retData, retCode:retCode, retHeaders:retHeaders}
end callRestPOSTAPIAndParseResults

on URLSession:tmpSession dataTask:tmpTask didReceiveData:tmpData
  set aRes to tmpTask’s response()
  
set retCode to aRes’s statusCode()
  
set retHeaders to aRes’s allHeaderFields()
  
  
set resStr to NSString’s alloc()’s initWithData:tmpData encoding:(NSUTF8StringEncoding)
  
set jsonString to NSString’s stringWithString:(resStr)
  
  
set jsonData to jsonString’s dataUsingEncoding:(NSUTF8StringEncoding)
  
set aJsonDict to NSJSONSerialization’s JSONObjectWithData:jsonData options:0 |error|:(missing value)
  
  
set retData to aJsonDict as list of string or string –as anything
end URLSession:dataTask:didReceiveData:

★Click Here to Open This Script 

Posted in JSON Network Record REST API URL | Tagged 10.11savvy 10.12savvy 10.13savvy | Leave a comment

AppStore.appをコントロール

Posted on 4月 16, 2018 by Takaaki Naganoya

macOS用のAppStore.appはmacOS 10.13搭載バージョンまではある程度(ちょっとだけ)AppleScriptからコントロール可能でした。macOS 10.14〜11.0搭載のAppStore.app V3.0ではコントロールできなくなっています。

もちろん、強引にGUI Scriptingで操作することは可能ですし、アプリケーション自体のAppleScript用語辞書に依存しないURL Event(open location)で指定のアプリケーションを表示状態にすることは可能です。

AppleScript名:AppStore.appをコントロール
tell application "App Store"
  properties
  
–> {frontmost:false, class:application, name:"App Store", version:"1.0"}
  
  
tell window 1
    properties
    
–> {document:missing value, closeable:true, zoomed:false, class:window, index:1, visible:true, name:"", modal:false, miniaturizable:true, titled:true, miniaturized:false, floating:false, id:55, resizable:true, bounds:{356, 22, 1843, 1161}, zoomable:true}
  end tell
  
end tell

delay 3

–指定のURLをオープンする
open location "macappstore://itunes.apple.com/jp/app/double-pdf/id1243418387?mt=12"
–> App Storeで指定のURLを表示

★Click Here to Open This Script 

Posted in URL | Tagged 10.11savvy 10.12savvy 10.13savvy App Store | Leave a comment

file URL文字列からPOSIX pathに変換 v2

Posted on 3月 31, 2018 by Takaaki Naganoya

“file://”ではじまるfile URL文字列からPOSIX pathに変換するAppleScriptの改修版です。

たまたまScripting Bridge経由でFinder上の選択中のファイル一覧を取得したら、”file://”ではじまる文字列が返ってきて、Cocoaに変換する機能がないか調べたものの….ない。

・・・と思ったら「あるよ」というのを教えてもらいました。

下調べして「path()」でNSURLから変換できるというのは調査してあったんですが、試行錯誤する中でうまくいかなかった記憶が…..結果的には教えていただいた方法で無事変換できた次第です。

AppleScript名:file URL文字列からPOSIX pathに変換 v2
use AppleScript version "2.4"
use framework "Foundation"
use scripting additions

set aStr to "file:///Users/me/Desktop/%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%BC%E3%83%B3%E3%82%B7%E3%83%A7%E3%83%83%E3%83%88%202018-03-12%2015.22.44.png"

set aURL to (current application’s |NSURL|’s URLWithString:aStr)
set aPOSIX to aURL’s |path|() as string

★Click Here to Open This Script 

Posted in File path Text URL | Tagged 10.11savvy 10.12savvy 10.13savvy | Leave a comment

指定PDFの全ページからリンクアノテーションのURLを取得してURLを書きかえる

Posted on 3月 28, 2018 by Takaaki Naganoya

指定PDFの全ページのURLリンクアノテーションのURLを書き換えるAppleScriptです。

URL Linkアノテーションを添付したPDFに対して、Linkアノテーションのboundsを取得して削除し、同じboundsの異なるURLへのリンクアノテーションを作成して保存します。

Keynote、Pages、NumbersのiWorkアプリケーションにはオブジェクトに対してリンクを付加し、URLを指定することができるようになっていますが、URLスキームはhttpがデフォルトで指定されています。

Pages上でURLスキームを指定できたら、それはそれで使い道がいろいろありそうですが、リクエストを出してもここはhttp(かmailto)以外は有効になる気配がありません。

そこで、URLだけダミーのものをこれらのiWorkアプリケーション上で割り振っておいていったんPDF書き出しを行い、書き出されたPDFのLinkアノテーションをあとでAppleScriptから書き換えることで、任意のURLリンクを埋め込むのと同じことができるようになるだろう、と考えて実験してみました。

ただ、1つのグラフィックオブジェクトに対してKeynote上でリンクを付与してPDF書き出しすると、Keynoteがオブジェクトの領域を細分化してリンクを作成するようです。文字とグラフィックにリンクを指定しただけなのに、やたらと大量のリンクアノテーションが検出されるので、念のためにチェックしてみたらこんな(↓)感じでした。

AppleScript名:指定PDFの全ページからリンクアノテーションのURLを取得してURLを書きかえる
— Created 2017-06-08 by Takaaki Naganoya
— Modified 2018-03-14 by Takaaki Naganoya
— 2017, 2018 Piyomaru Software
use AppleScript version "2.5"
use scripting additions
use framework "Foundation"
use framework "Quartz"

property |NSURL| : a reference to current application’s |NSURL|
property PDFActionURL : a reference to current application’s PDFActionURL
property PDFDocument : a reference to current application’s PDFDocument
property PDFAnnotationLink : a reference to current application’s PDFAnnotationLink

set aPOSIX to POSIX path of (choose file of type {"com.adobe.pdf"} with prompt "Choose a PDF with Annotation")
set linkList to replaceLinkURLFromPDF(aPOSIX, "http://www.apple.com/jp", "applescript://com.apple.scripteditor?action=new&script=display%20dialog%20%22TEST%22") of me

on replaceLinkURLFromPDF(aPOSIX, origURL, toURL)
  set v2 to system attribute "sys2" –> case: macOS 10.12 =12
  
  
set aURL to (|NSURL|’s fileURLWithPath:aPOSIX)
  
set aPDFdoc to PDFDocument’s alloc()’s initWithURL:aURL
  
set pCount to aPDFdoc’s pageCount()
  
  
–PDFのページ(PDFPage)でループ
  
repeat with ii from 0 to (pCount – 1)
    set tmpPage to (aPDFdoc’s pageAtIndex:ii) –PDFPage
    
    
set anoList to (tmpPage’s annotations()) as list
    
if anoList is not equal to {missing value} then –指定PDF中にAnotationが存在した
      
      
–対象PDFPage内で検出されたAnnotationでループ
      
repeat with i in anoList
        if v2 < 13 then
          set aType to (i’s type()) as string –to macOS Sierra (10.10, 10.11 & 10.12)
        else
          set aType to (i’s |Type|()) as string –macOS High Sierra (10.13) or later
        end if
        
        
–Link Annotationの削除と同様のサイズでLink Annotationの新規作成
        
if aType = "Link" then
          set tmpURL to (i’s |URL|()’s absoluteString()) as string
          
          
if tmpURL = origURL then
            set theBounds to i’s |bounds|() –削除する前にLink Annotationの位置情報を取得
            
–> {origin:{x:78.65625, y:454.7188}, size:{width:96.96875, height:4.0937}}
            
            (
tmpPage’s removeAnnotation:i) –PDFPageから指定のLink Annotationを削除  
            
            
set theLink to (PDFAnnotationLink’s alloc()’s initWithBounds:theBounds)
            
set theAction to (PDFActionURL’s alloc()’s initWithURL:(current application’s |NSURL|’s URLWithString:toURL))
            (
theLink’s setMouseUpAction:theAction)
            (
tmpPage’s addAnnotation:theLink)
            
            
log {ii + 1, theBounds, origURL}
          end if
        end if
      end repeat
    end if
  end repeat
  
  
return (aPDFdoc’s writeToFile:aPOSIX) as boolean
end replaceLinkURLFromPDF

★Click Here to Open This Script 

Posted in file PDF URL | Tagged 10.11savvy 10.12savvy 10.13savvy | 1 Comment

URLとrecordからパラメータつきURLを生成する v3

Posted on 3月 19, 2018 by Takaaki Naganoya

指定のURLに対して、recordで指定したパラメータつきURLを生成するAppleScriptです。

REST APIを呼び出すときに使っているため、登場頻度がきわめて高いサブルーチンです。OLD Style AppleScriptの機能の範囲内で書くこともできますが、Cocoaの機能を用いたほうが圧倒的に手軽に書ける好例といえます。

AppleScript名:URLとrecordからパラメータつきURLを生成する v3
— Created 2016-04-19 by Takaaki Naganoya
— Modified 2016-11-12 by Takaaki Naganoya
— 2016 Piyomaru Software
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"

property NSURLQueryItem : a reference to current application’s NSURLQueryItem
property NSMutableDictionary : a reference to current application’s NSMutableDictionary
property NSURLComponents : a reference to current application’s NSURLComponents

set aBaseURL to "https://slack.com/api/auth.test"
set aRec to {token:"aaaa", channel:"ぴよまるソフトウェア"}
set aRes to retURLwithParams(aBaseURL, aRec) of me
–>  "https://slack.com/api/auth.test?token=aaaa&channel=%E3%81%B4%E3%82%88%E3%81%BE%E3%82%8B%E3%82%BD%E3%83%95%E3%83%88%E3%82%A6%E3%82%A7%E3%82%A2"

on retURLwithParams(aBaseURL as string, aRec as record)
  set aDic to NSMutableDictionary’s dictionaryWithDictionary:aRec
  
  
set aKeyList to (aDic’s allKeys()) as list
  
set aValList to (aDic’s allValues()) as list
  
set aLen to length of aKeyList
  
set bLen to length of aValList
  
if aLen is not equal to bLen then return false
  
  
set qList to {}
  
repeat with i from 1 to aLen
    set aName to (contents of item i of aKeyList) as string
    
set aVal to (contents of item i of aValList) as string
    
set the end of qList to (NSURLQueryItem’s queryItemWithName:aName value:aVal)
  end repeat
  
  
set aComp to NSURLComponents’s alloc()’s initWithString:aBaseURL
  
aComp’s setQueryItems:qList
  
set aURL to (aComp’s |URL|()’s absoluteString()) as text
  
  
return aURL
end retURLwithParams

★Click Here to Open This Script 

Posted in Record URL | Tagged 10.11savvy 10.12savvy 10.13savvy | Leave a comment

指定PDFの全ページからリンクアノテーションのURLを取得(http)v2

Posted on 2月 14, 2018 by Takaaki Naganoya

指定のPDFの全ページを走査してリンクアノテーションのURLのうちURL Schemeが”http”のものを抽出し、”http://piyocast.com/as/archives/” を含むURLを取得するAppleScriptです。

macOS 10.13以降でも動作するようにしてあります。電子書籍のPDFから本Blogへのリンクを張ってある箇所を検出するために作成したものです。

AppleScript名:指定PDFの全ページからリンクアノテーションのURLを取得(http)v2
— Created 2017-06-08 by Takaaki Naganoya
— Modified 2018-02-14 by Takaaki Naganoya
— 2017 Piyomaru Software
use AppleScript version "2.5"
use scripting additions
use framework "Foundation"
use framework "Quartz"

property |NSURL| : a reference to current application’s |NSURL|
property PDFDocument : a reference to current application’s PDFDocument

set aPOSIX to POSIX path of (choose file of type {"com.adobe.pdf"} with prompt "Choose a PDF with Annotation")
set linkList to getLinkURLFromPDF(aPOSIX, "http", "http://piyocast.com/as/archives/") of me
–>  {​​​​​{​​​​​​​pageNum:39, ​​​​​​​linkURL:"http://piyocast.com/as/archives/69"​​​​​},….}

on getLinkURLFromPDF(aPOSIX, urlScheme, condURL)
  set v2 to system attribute "sys2" –> macOS 10.12 =12
  
  
set aURL to (|NSURL|’s fileURLWithPath:aPOSIX)
  
  
set aPDFdoc to PDFDocument’s alloc()’s initWithURL:aURL
  
set pCount to aPDFdoc’s pageCount()
  
  
set outList to {}
  
  
–PDFのページでループ
  
repeat with ii from 0 to (pCount – 1)
    set tmpPage to (aPDFdoc’s pageAtIndex:ii)
    
    
set anoList to (tmpPage’s annotations()) as list
    
if anoList is not equal to {missing value} then –指定PDF中にAnotationが存在した
      
      
–対象PDFPage内で検出されたAnnotationでループ
      
repeat with i in anoList
        
        
if v2 < 13 then
          –to macOS Sierra
          
set aType to (i’s type()) as string
        else
          –macOS High Sierra or later
          
set aType to (i’s |Type|()) as string
        end if
        
        
—
        
if aType = "Link" then
          set tmpURL to i’s |URL|()
          
if tmpURL is not equal to missing value then
            set tmpScheme to (tmpURL’s |scheme|()) as string
            
if tmpScheme = urlScheme then
              set urlStr to (tmpURL’s absoluteString()) as string
              
              
if (urlStr contains condURL) then
                set the end of outList to {pageNum:(ii + 1), linkURL:urlStr}
              end if
              
            end if
          end if
        end if
      end repeat
    end if
  end repeat
  
  
return outList
end getLinkURLFromPDF

★Click Here to Open This Script 

Posted in PDF URL | Tagged 10.11savvy 10.12savvy 10.13savvy | Leave a comment

指定PDFの全ページからリンクアノテーションのURLを取得(applescript) v2

Posted on 2月 14, 2018 by Takaaki Naganoya

指定のPDFの全ページを走査してリンクアノテーションのURLのうちURL Schemeが”applescript”のものを抽出して、Link Scriptの内容を取得するAppleScriptです。

macOS 10.13以降でも動作するようにしてあります。

AppleScript名:指定PDFの全ページからリンクアノテーションのURLを取得(applescript) v2
— Created 2017-06-08 by Takaaki Naganoya
— Modified 2018-02-14 by Takaaki Naganoya
— 2017 Piyomaru Software
use AppleScript version "2.5"
use scripting additions
use framework "Foundation"
use framework "Quartz"

property |NSURL| : a reference to current application’s |NSURL|
property NSString : a reference to current application’s NSString
property PDFDocument : a reference to current application’s PDFDocument
property NSMutableDictionary : a reference to current application’s NSMutableDictionary
property NSUTF8StringEncoding : a reference to current application’s NSUTF8StringEncoding

set aPOSIX to POSIX path of (choose file of type {"com.adobe.pdf"} with prompt "Choose a PDF with Annotation")
set linkList to getLinkURLFromPDF(aPOSIX, "applescript") of me
–>  {​​​​​{​​​​​​​pageNum:95, ​​​​​​​linkScript:"set aStr to \"ぴよまるソフトウェア\" set aPath to choose file name……}}

on getLinkURLFromPDF(aPOSIX, urlScheme)
  set v2 to system attribute "sys2" –> macOS 10.12 =12
  
  
set aURL to (|NSURL|’s fileURLWithPath:aPOSIX)
  
  
set aPDFdoc to PDFDocument’s alloc()’s initWithURL:aURL
  
set pCount to aPDFdoc’s pageCount()
  
  
set outList to {}
  
  
–PDFのページでループ
  
repeat with ii from 0 to (pCount – 1)
    set tmpPage to (aPDFdoc’s pageAtIndex:ii)
    
    
set anoList to (tmpPage’s annotations()) as list
    
if anoList is not equal to {missing value} then –指定PDF中にAnotationが存在した
      
      
–対象PDFPage内で検出されたAnnotationでループ
      
repeat with i in anoList
        
        
if v2 < 13 then
          –to macOS Sierra
          
set aType to (i’s type()) as string
        else
          –macOS High Sierra or later
          
set aType to (i’s |Type|()) as string
        end if
        
        
—
        
if aType = "Link" then
          set tmpURL to i’s |URL|()
          
if tmpURL is not equal to missing value then
            set tmpScheme to (tmpURL’s |scheme|()) as string
            
            
if tmpScheme = urlScheme then
              set urlStr to (tmpURL’s absoluteString()) as string
              
set urlRec to parseQueryDictFromURLString(urlStr) of me
              
set tmpScript to (urlRec’s |script|) as string
              
              
set the end of outList to {pageNum:(ii + 1), linkScript:tmpScript}
              
            end if
          end if
        end if
      end repeat
    end if
  end repeat
  
  
return outList
end getLinkURLFromPDF

on parseQueryDictFromURLString(aURLStr as string)
  if aURLStr = "" then error "No URL String"
  
  
set aURL to |NSURL|’s URLWithString:aURLStr
  
set aQuery to aURL’s query() –Get Query string part from URL
  
if aQuery’s |length|() = 0 then return false
  
  
set aDict to NSMutableDictionary’s alloc()’s init()
  
set aParamList to (aQuery’s componentsSeparatedByString:"&") as list
  
  
repeat with i in aParamList
    set j to contents of i
    
if length of j > 0 then
      set tmpStr to (NSString’s stringWithString:j)
      
set eList to (tmpStr’s componentsSeparatedByString:"=")
      
set anElement to (eList’s firstObject()’s stringByReplacingPercentEscapesUsingEncoding:(NSUTF8StringEncoding))
      
set aValStr to (eList’s lastObject()’s stringByReplacingPercentEscapesUsingEncoding:(NSUTF8StringEncoding))
      (
aDict’s setObject:aValStr forKey:anElement)
    end if
  end repeat
  
  
return aDict
end parseQueryDictFromURLString

★Click Here to Open This Script 

Posted in PDF URL | Tagged 10.11savvy 10.12savvy 10.13savvy | Leave a comment

Post navigation

  • Older posts
  • Newer posts

電子書籍(PDF)をオンラインストアで販売中!

Google Search

Popular posts

  • 開発機としてM2 Mac miniが来たのでガチレビュー
  • macOS 13.6.5 AS系のバグ、一切直らず
  • CotEditorで2つの書類の行単位での差分検出
  • Apple純正マウス、キーボードのバッテリー残量取得
  • macOS 15, Sequoia
  • 初心者がつまづきやすい「log」コマンド
  • 指定のWordファイルをPDFに書き出す
  • Adobe AcrobatをAppleScriptから操作してPDF圧縮
  • メキシカンハットの描画
  • 与えられた文字列の1D Listのすべての順列組み合わせパターン文字列を返す v3(ベンチマーク用)
  • Pages本執筆中に、2つの書類モード切り替えに気がついた
  • 2023年に書いた価値あるAppleScript
  • Pixelmator Pro v3.6.4でAppleScriptからの操作時の挙動に違和感が
  • AdobeがInDesign v19.4からPOSIX pathを採用
  • Safariで「プロファイル」機能を使うとAppleScriptの処理に影響
  • Cocoa Scripting Course 続刊計画
  • Numbersで選択範囲のセルの前後の空白を削除
  • NaturalLanguage.frameworkでNLEmbeddingの処理が可能な言語をチェック
  • macOS 14.xでScript Menuの実行速度が大幅に下がるバグ
  • AppleScriptによる並列処理

Tags

10.11savvy (1102) 10.12savvy (1243) 10.13savvy (1392) 10.14savvy (587) 10.15savvy (438) 11.0savvy (283) 12.0savvy (207) 13.0savvy (177) 14.0savvy (127) 15.0savvy (104) CotEditor (64) Finder (51) iTunes (19) Keynote (115) NSAlert (61) NSArray (51) NSBitmapImageRep (20) NSBundle (20) NSButton (34) NSColor (53) NSDictionary (28) NSFileManager (23) NSFont (21) NSImage (41) NSJSONSerialization (21) NSMutableArray (63) NSMutableDictionary (22) NSPredicate (36) NSRunningApplication (56) NSScreen (30) NSScrollView (22) NSString (119) NSURL (98) NSURLRequest (23) NSUTF8StringEncoding (30) NSView (33) NSWorkspace (20) Numbers (74) Pages (54) Safari (44) Script Editor (27) WKUserContentController (21) WKUserScript (20) WKWebView (23) WKWebViewConfiguration (22)

カテゴリー

  • 2D Bin Packing
  • 3D
  • AirDrop
  • AirPlay
  • Animation
  • AppleScript Application on Xcode
  • Beginner
  • Benchmark
  • beta
  • Bluetooth
  • Books
  • boolean
  • bounds
  • Bug
  • Calendar
  • call by reference
  • check sum
  • Clipboard
  • Cocoa-AppleScript Applet
  • Code Sign
  • Color
  • Custom Class
  • dialog
  • diff
  • drive
  • Droplet
  • exif
  • file
  • File path
  • filter
  • folder
  • Font
  • Font
  • GAME
  • geolocation
  • GUI
  • GUI Scripting
  • Hex
  • History
  • How To
  • iCloud
  • Icon
  • Image
  • Input Method
  • Internet
  • iOS App
  • JavaScript
  • JSON
  • JXA
  • Keychain
  • Keychain
  • Language
  • Library
  • list
  • Locale
  • Localize
  • Machine Learning
  • Map
  • Markdown
  • Menu
  • Metadata
  • MIDI
  • MIME
  • Natural Language Processing
  • Network
  • news
  • Noification
  • Notarization
  • Number
  • Object control
  • OCR
  • OSA
  • parallel processing
  • PDF
  • Peripheral
  • PRODUCTS
  • QR Code
  • Raw AppleEvent Code
  • Record
  • rectangle
  • recursive call
  • regexp
  • Release
  • Remote Control
  • Require Control-Command-R to run
  • REST API
  • Review
  • RTF
  • Sandbox
  • Screen Saver
  • Script Libraries
  • sdef
  • search
  • Security
  • selection
  • shell script
  • Shortcuts Workflow
  • Sort
  • Sound
  • Spellchecker
  • Spotlight
  • SVG
  • System
  • Tag
  • Telephony
  • Text
  • Text to Speech
  • timezone
  • Tools
  • Update
  • URL
  • UTI
  • Web Contents Control
  • WiFi
  • XML
  • XML-RPC
  • イベント(Event)
  • 未分類

アーカイブ

  • 2025年1月
  • 2024年12月
  • 2024年11月
  • 2024年10月
  • 2024年9月
  • 2024年8月
  • 2024年7月
  • 2024年6月
  • 2024年5月
  • 2024年4月
  • 2024年3月
  • 2024年2月
  • 2024年1月
  • 2023年12月
  • 2023年11月
  • 2023年10月
  • 2023年9月
  • 2023年8月
  • 2023年7月
  • 2023年6月
  • 2023年5月
  • 2023年4月
  • 2023年3月
  • 2023年2月
  • 2023年1月
  • 2022年12月
  • 2022年11月
  • 2022年10月
  • 2022年9月
  • 2022年8月
  • 2022年7月
  • 2022年6月
  • 2022年5月
  • 2022年4月
  • 2022年3月
  • 2022年2月
  • 2022年1月
  • 2021年12月
  • 2021年11月
  • 2021年10月
  • 2021年9月
  • 2021年8月
  • 2021年7月
  • 2021年6月
  • 2021年5月
  • 2021年4月
  • 2021年3月
  • 2021年2月
  • 2021年1月
  • 2020年12月
  • 2020年11月
  • 2020年10月
  • 2020年9月
  • 2020年8月
  • 2020年7月
  • 2020年6月
  • 2020年5月
  • 2020年4月
  • 2020年3月
  • 2020年2月
  • 2020年1月
  • 2019年12月
  • 2019年11月
  • 2019年10月
  • 2019年9月
  • 2019年8月
  • 2019年7月
  • 2019年6月
  • 2019年5月
  • 2019年4月
  • 2019年3月
  • 2019年2月
  • 2019年1月
  • 2018年12月
  • 2018年11月
  • 2018年10月
  • 2018年9月
  • 2018年8月
  • 2018年7月
  • 2018年6月
  • 2018年5月
  • 2018年4月
  • 2018年3月
  • 2018年2月

https://piyomarusoft.booth.pm/items/301502

メタ情報

  • ログイン
  • 投稿フィード
  • コメントフィード
  • WordPress.org

Forum Posts

  • 人気のトピック
  • 返信がないトピック

メタ情報

  • ログイン
  • 投稿フィード
  • コメントフィード
  • WordPress.org
Proudly powered by WordPress
Theme: Flint by Star Verte LLC