# [メタ情報] # 識別子: appsheet_monitor_exe # 補足: AppSheet等からの要求を検知し、安全基準に従ってログ出力する監視用テストスクリプト。 # 状態: 公開用 # [/メタ情報] 要約: このスクリプトは、外出先のスマートフォン(AppSheet等)からGoogleスプレッドシートを経由して登録された処理要求を、Macローカル環境で安全かつ定期的に自動実行・監視するためのPythonプログラムです。常時稼働の「5分おきの監視ループ」によって、シート内のStatus列が「未処理」となっている行を検出し、Command列に記述された処理内容を速やかに読み取ります。現在は独自のハイブリッド安全基準を厳格に適用中であり、予期せぬ破壊的コマンドの実行を未然に防ぐため、実際のシステム操作は行わず、実行内容を安全にターミナルへログ出力する「テスト実行」にとどめています。ログ出力が完了すると、該当行のStatusを即座に「完了」へ更新し、Log列へ実行完了日時とメッセージを書き戻して一連のプロセスを終えます。APIキー等の機密情報はすべて環境変数として別途安全に管理されています。 === Python(appsheet_monitor.py) === /Users/[YOUR_USERNAME]/[YOUR_PATH]/appsheet_monitor.py ```python import os import time import logging import gspread from datetime import datetime from pathlib import Path from dotenv import load_dotenv # ログの設定 logging.basicConfig( level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s' ) # ====== 金庫(.env)の動的ロードを追加 ====== load_dotenv(Path.home() / "python_scripts" / ".env") # .env から認証情報とスプレッドシートIDを取得 # 動的ユーザー名解決や絶対パスを考慮 CREDENTIALS_PATH = os.getenv('<固有のランダム文字列>') if CREDENTIALS_PATH and not os.path.isabs(CREDENTIALS_PATH): CREDENTIALS_PATH = str(Path.home() / CREDENTIALS_PATH) SHEET_ID = os.getenv('APPSHEET_TASK_SHEET_ID') WORKSHEET_NAME = 'TaskQueue' # 対象とするシート名 def process_tasks(): try: # 認証とクライアントの初期化 if not CREDENTIALS_PATH or not SHEET_ID: logging.error("環境変数 GOOGLE_APPLICATION_CREDENTIALS または APPSHEET_TASK_SHEET_ID が設定されていません。") return gc = gspread.service_account(filename=CREDENTIALS_PATH) spreadsheet = gc.open_by_key(SHEET_ID) worksheet = spreadsheet.worksheet(WORKSHEET_NAME) # すべてのレコードを取得(1行目はヘッダーとして扱われます) records = worksheet.get_all_records() headers = worksheet.row_values(1) # ヘッダーの中に Status, Command, Log 列が存在するか確認 if 'Status' not in headers or 'Command' not in headers or 'Log' not in headers: logging.error("シートに 'Status', 'Command', 'Log' のいずれかの列が見つかりません。") return status_col = headers.index('Status') + 1 log_col = headers.index('Log') + 1 # データ行(2行目以降)を一つずつ確認 for i, row in enumerate(records, start=2): status = row.get('Status', '') if status == '未処理': command = row.get('Command', '') logging.info(f"未処理のタスクを発見しました。行: {i}, コマンド: {command}") # 【ハイブリッド安全基準の適用】 # 実際の実行は行わず、安全なテスト実行としてエコー(ログ出力)のみを実施します。 # 破壊的なコマンド(rmなど)や予期しない操作を防ぎます。 logging.info(f"[安全テスト実行] 以下のコマンドを実行したと仮定します: {command}") # 完了時刻とメッセージの作成 now_str = datetime.now().strftime('%Y-%m-%d %H:%M:%S') log_message = f"実行完了(安全テスト): {command}" # Statusを '完了' に更新 worksheet.update_cell(i, status_col, '完了') # Logに実行記録を更新 worksheet.update_cell(i, log_col, f"{now_str} - {log_message}") logging.info(f"行 {i} のステータスを '完了' に更新しました。") except Exception as e: logging.error(f"監視処理中にエラーが発生しました: {e}") def main(): logging.info("AppSheet監視スクリプトを起動しました。5分おきにチェックを実行します。") # 無限ループで定期実行(5分 = 300秒ごと) while True: process_tasks() time.sleep(300) if __name__ == "__main__": main() ```