# [メタ情報] # 識別子: geminiを使った日本語要約_exe # システム名: 未分類 # 技術種別: Misc # 機能名: Misc # 使用言語: AppleScript # 状態: 実行用 # [/メタ情報] 要約:このAppleScriptスクリプトは、ユーザーが選択したテキストをGoogle Gemini APIで要約し、その結果と元の本文をファイルに保存する自動化ツールです。右クリックで選択されたテキストを受け取り、処理前に内容をプレビューして続行を確認します。スクリプトはPython3とcurlコマンドを使い、Gemini APIにアクセス。`MY_API_KEY`を用いて、利用可能なモデルから`generateContent`をサポートするモデルを自動選択し、指定された文字数(MAX_SUMMARY_CHARS)で日本語の要約をリクエストします。Geminiからの出力は、要約文とその後に続く元の本文で構成されます。処理が完了すると、`SAVE_DIR`にタイムスタンプと要約の最初の行から生成されたタイトル(TITLE_MAX_CHARSで制限、ファイル名として安全化)を含むUTF-8形式のテキストファイルとして保存されます。処理状況やエラーは通知でユーザーに伝えられます。 Automator ワークフローが受け取る項目:テキスト 選択肢:すべてのアプリケーション ``` -- ===== 設定 ===== property MY_API_KEY : "<あなたのgemini API KEY>" property SAVE_DIR : "/Volumes/NO3_SSD/Dropbox/dropbox_1/Geminiによる要約と本文/" property MAX_SUMMARY_CHARS : 400 property TITLE_MAX_CHARS : 40 -- ================= on run {input, parameters} -- 右クリックで選択されたテキストを直接取得 set clipText to (input as string) if clipText is "" then display alert "テキストが選択されていません" return input end if -- 1) 保存確認(プレビュー) set previewLimit to my minInt(120, length of clipText) set previewText to text 1 thru previewLimit of clipText set dlg to display dialog "この内容をGeminiで保存しますか?" & return & return & previewText buttons {"キャンセル", "続行"} default button "続行" if button returned of dlg is "キャンセル" then return input -- 2) Gemini API 処理 display notification "利用可能なモデルを自動選択して要約中..." with title "通信開始" set finalResult to my askGemini(clipText) if finalResult starts with "Error:" then display alert "Gemini APIエラー" message finalResult return input end if -- 3) 保存処理 my ensureDir(SAVE_DIR) set tsFile to do shell script "date +%Y%m%d-%H%M%S" set tsHuman to do shell script "date '+%Y-%m-%d %H:%M:%S'" -- タイトル:要約の最初の1行を使用 set firstLine to item 1 of my splitByNewline(finalResult) set userTitle to my sanitizeFilename(my clampLen(firstLine, TITLE_MAX_CHARS)) set filename to tsFile & "_" & userTitle & ".txt" my writeTextUTF8(SAVE_DIR & filename, "[" & tsHuman & "]" & return & return & finalResult) display notification filename with title "保存完了" return input end run on askGemini(targetText) set promptText to "以下のテキストを日本語で" & (MAX_SUMMARY_CHARS as string) & "字以内に要約してください。出力は要約文のみ。その後に続けて元の本文も出力してください。" & return & return set fullInput to promptText & targetText set tempInputPath to "/tmp/gemini_in.txt" try my writeTextUTF8(tempInputPath, fullInput) set pyCode to "import json, sys, subprocess try: # 1. 利用可能なモデルをリストから探す list_url = 'https://generativelanguage.googleapis.com/v1beta/models?key=" & MY_API_KEY & "' list_res = subprocess.run(['curl', '-s', list_url], capture_output=True, text=True) models_data = json.loads(list_res.stdout) # generateContentをサポートしているモデルを探す chosen_model = next((m['name'] for m in models_data.get('models', []) if 'generateContent' in m.get('supportedGenerationMethods', [])), None) if not chosen_model: print('Error: 使用可能なモデルが見つかりません') sys.exit() # 2. 選んだモデルで実行 with open('" & tempInputPath & "', 'r', encoding='utf-8') as f: content = f.read() payload = json.dumps({'contents': [{'parts': [{'text': content}]}]}) exec_url = f'https://generativelanguage.googleapis.com/v1beta/{chosen_model}:generateContent?key=" & MY_API_KEY & "' res = subprocess.run(['curl', '-s', '-X', 'POST', '-H', 'Content-Type: application/json', '-d', payload, exec_url], capture_output=True, text=True) data = json.loads(res.stdout) if 'candidates' in data: print(data['candidates'][0]['content']['parts'][0]['text']) else: print('Error: ' + data.get('error', {}).get('message', '不明なエラー')) except Exception as e: print('Error: ' + str(e))" return do shell script "python3 -c " & quoted form of pyCode on error errMsg return "Error: 通信失敗 " & errMsg end try end askGemini -- ユーティリティ on ensureDir(d) do shell script "mkdir -p " & quoted form of d end ensureDir on writeTextUTF8(p, t) try set fRef to open for access (POSIX file p) with write permission set eof of fRef to 0 write t to fRef as «class utf8» close access fRef on error try close access (POSIX file p) end try end try end writeTextUTF8 on splitByNewline(s) set AppleScript's text item delimiters to {return, linefeed} set arr to text items of s set AppleScript's text item delimiters to "" return arr end splitByNewline on sanitizeFilename(s) set illegal to "/:\\*?\"<>| " & linefeed & return set out to "" repeat with ch in characters of s if contents of ch is in illegal then set out to out & "_" else set out to out & ch end if end repeat if out is "" then set out to "無題" return out end sanitizeFilename on clampLen(s, n) if (length of s) ≤ n then return s return text 1 thru n of s end clampLen on minInt(a, b) if a < b then return a return b end minInt ```