{
  "nodes": [
    {
      "id": "f3d745a2-edd3-4d86-bcfc-ff772f0dd9bd",
      "name": "Real-time Check (5 min)",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        -5472,
        944
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "minutes"
            }
          ]
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "7a27519a-2676-4215-8589-769cbd2e05ef",
      "name": "Daily Predictions (6 AM)",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        -5472,
        1744
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "triggerAtHour": 6
            }
          ]
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "74b0a74f-17eb-4363-a7e8-4dbfc2b8a7f9",
      "name": "Weekly Report (Sunday 8 PM)",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        -5472,
        2368
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "daysInterval": 7,
              "triggerAtHour": 20
            }
          ]
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "6d10a256-3f6f-4a9d-97ba-e4b9b0f28e77",
      "name": "Get ISS Detailed (N2YO)",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -4992,
        944
      ],
      "parameters": {
        "url": "=https://api.n2yo.com/rest/v1/satellite/positions/25544/{{ $vars.USER_LAT }}/{{ $vars.USER_LON }}/0/1/?apiKey={{ $vars.N2YO_API_KEY }}",
        "options": {}
      },
      "typeVersion": 4.2
    },
    {
      "id": "e80a14fe-57ba-4eab-ac37-2b5d0197c80d",
      "name": "Get Tiangong Position",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -4992,
        1104
      ],
      "parameters": {
        "url": "=https://api.n2yo.com/rest/v1/satellite/positions/48274/{{ $vars.USER_LAT }}/{{ $vars.USER_LON }}/0/1/?apiKey={{ $vars.N2YO_API_KEY }}",
        "options": {}
      },
      "typeVersion": 4.2
    },
    {
      "id": "46f850f4-73b6-455f-84f9-53f6486742b4",
      "name": "Calculate All Satellites",
      "type": "n8n-nodes-base.code",
      "position": [
        -4752,
        1024
      ],
      "parameters": {
        "jsCode": "// YOUR_AWS_SECRET_KEY_HERE===\n// USER CONFIGURATION via Environment Variables\n// YOUR_AWS_SECRET_KEY_HERE===\nconst USER_LAT = parseFloat($vars.USER_LAT) || 35.6762;\nconst USER_LON = parseFloat($vars.USER_LON) || 139.6503;\nconst USER_LOCATION_NAME = $vars.USER_LOCATION_NAME || 'Tokyo';\nconst VISIBILITY_RADIUS_KM = parseInt($vars.VISIBILITY_RADIUS_KM) || 800;\n\n// YOUR_AWS_SECRET_KEY_HERE===\n// Get data from previous nodes\n// YOUR_AWS_SECRET_KEY_HERE===\nconst issBasic = $('Get ISS Position1').first().json;\nconst issDetailed = $('Get ISS Detailed (N2YO)').first().json;\nconst tiangong = $('Get Tiangong Position').first().json;\n\n// YOUR_AWS_SECRET_KEY_HERE===\n// Haversine formula for distance calculation\n// YOUR_AWS_SECRET_KEY_HERE===\nfunction haversineDistance(lat1, lon1, lat2, lon2) {\n  const R = 6371;\n  const dLat = (lat2 - lat1) * Math.PI / 180;\n  const dLon = (lon2 - lon1) * Math.PI / 180;\n  const a = \n    Math.sin(dLat/2) * Math.sin(dLat/2) +\n    Math.cos(lat1 * Math.PI / 180) * Math.cos(lat2 * Math.PI / 180) * \n    Math.sin(dLon/2) * Math.sin(dLon/2);\n  const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));\n  return R * c;\n}\n\n// YOUR_AWS_SECRET_KEY_HERE===\n// Calculate bearing (compass direction)\n// YOUR_AWS_SECRET_KEY_HERE===\nfunction calculateBearing(lat1, lon1, lat2, lon2) {\n  const \u03c61 = lat1 * Math.PI / 180;\n  const \u03c62 = lat2 * Math.PI / 180;\n  const \u0394\u03bb = (lon2 - lon1) * Math.PI / 180;\n  const y = Math.sin(\u0394\u03bb) * Math.cos(\u03c62);\n  const x = Math.cos(\u03c61) * Math.sin(\u03c62) - Math.sin(\u03c61) * Math.cos(\u03c62) * Math.cos(\u0394\u03bb);\n  const \u03b8 = Math.atan2(y, x);\n  return (\u03b8 * 180 / Math.PI + 360) % 360;\n}\n\nfunction bearingToDirection(bearing) {\n  const directions = ['N', 'NE', 'E', 'SE', 'S', 'SW', 'W', 'NW'];\n  return directions[Math.round(bearing / 45) % 8];\n}\n\nfunction bearingToDirectionJP(bearing) {\n  const directions = ['\u5317', '\u5317\u6771', '\u6771', '\u5357\u6771', '\u5357', '\u5357\u897f', '\u897f', '\u5317\u897f'];\n  return directions[Math.round(bearing / 45) % 8];\n}\n\n// YOUR_AWS_SECRET_KEY_HERE===\n// Process ISS\n// YOUR_AWS_SECRET_KEY_HERE===\nconst issLat = parseFloat(issBasic.iss_position.latitude);\nconst issLon = parseFloat(issBasic.iss_position.longitude);\nconst issDistance = haversineDistance(USER_LAT, USER_LON, issLat, issLon);\nconst issBearing = calculateBearing(USER_LAT, USER_LON, issLat, issLon);\nconst issAltitude = issDetailed?.positions?.[0]?.sataltitude || 408;\nconst issElevation = Math.atan2(issAltitude, issDistance) * 180 / Math.PI;\n\n// YOUR_AWS_SECRET_KEY_HERE===\n// Process Tiangong\n// YOUR_AWS_SECRET_KEY_HERE===\nlet tiangongData = null;\nif (tiangong?.positions?.[0]) {\n  const tgPos = tiangong.positions[0];\n  const tgDistance = haversineDistance(USER_LAT, USER_LON, tgPos.satlatitude, tgPos.satlongitude);\n  const tgBearing = calculateBearing(USER_LAT, USER_LON, tgPos.satlatitude, tgPos.satlongitude);\n  tiangongData = {\n    name: 'Tiangong (\u5929\u5bae)',\n    noradId: 48274,\n    latitude: tgPos.satlatitude,\n    longitude: tgPos.satlongitude,\n    altitude: tgPos.sataltitude,\n    distance: Math.round(tgDistance),\n    bearing: Math.round(tgBearing),\n    direction: bearingToDirection(tgBearing),\n    directionJP: bearingToDirectionJP(tgBearing),\n    elevation: Math.round(Math.atan2(tgPos.sataltitude, tgDistance) * 180 / Math.PI),\n    isNearby: tgDistance <= VISIBILITY_RADIUS_KM\n  };\n}\n\nconst satellites = [\n  {\n    name: 'ISS (\u56fd\u969b\u5b87\u5b99\u30b9\u30c6\u30fc\u30b7\u30e7\u30f3)',\n    noradId: 25544,\n    latitude: issLat,\n    longitude: issLon,\n    altitude: Math.round(issAltitude),\n    distance: Math.round(issDistance),\n    bearing: Math.round(issBearing),\n    direction: bearingToDirection(issBearing),\n    directionJP: bearingToDirectionJP(issBearing),\n    elevation: Math.round(issElevation),\n    isNearby: issDistance <= VISIBILITY_RADIUS_KM,\n    velocity: issDetailed?.positions?.[0]?.velocity || 27600\n  }\n];\n\nif (tiangongData) satellites.push(tiangongData);\n\nconst nearbySatellites = satellites.filter(s => s.isNearby);\n\nreturn [{\n  json: {\n    satellites,\n    nearbySatellites,\n    hasNearby: nearbySatellites.length > 0,\n    userLocation: {\n      name: USER_LOCATION_NAME,\n      latitude: USER_LAT,\n      longitude: USER_LON\n    },\n    visibilityRadius: VISIBILITY_RADIUS_KM,\n    timestamp: new Date().toISOString(),\n    timestampJP: new Date().toLocaleString('ja-JP', { timeZone: 'Asia/Tokyo' })\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "783657de-3858-42d0-a672-a4f29c5b2ff6",
      "name": "Any Satellite Nearby?",
      "type": "n8n-nodes-base.if",
      "position": [
        -4512,
        1024
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "has-nearby",
              "operator": {
                "type": "boolean",
                "operation": "true"
              },
              "leftValue": "={{ $json.hasNearby }}",
              "rightValue": ""
            }
          ]
        }
      },
      "typeVersion": 2
    },
    {
      "id": "ea6d90bf-85c8-4648-a296-70132d3ab964",
      "name": "Get Weather Conditions",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -4272,
        944
      ],
      "parameters": {
        "url": "=https://api.openweathermap.org/data/2.5/weather?lat={{ $json.userLocation.latitude }}&lon={{ $json.userLocation.longitude }}&appid={{ $credentials.openWeatherMapApi.apiKey }}&units=metric",
        "options": {},
        "authentication": "predefinedCredentialType",
        "nodeCredentialType": "openWeatherMapApi"
      },
      "typeVersion": 4.2
    },
    {
      "id": "cb5579d6-8260-4904-91b1-8b51d5d61fe4",
      "name": "Get Astronauts in Space",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -4272,
        1104
      ],
      "parameters": {
        "url": "http://api.open-notify.org/astros.json",
        "options": {}
      },
      "typeVersion": 4.2
    },
    {
      "id": "e21082fe-9638-468a-a2cb-79aa59982e84",
      "name": "Observable Conditions?",
      "type": "n8n-nodes-base.if",
      "position": [
        -3792,
        1024
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "can-observe",
              "operator": {
                "type": "boolean",
                "operation": "true"
              },
              "leftValue": "={{ $json.observation.canObserve }}",
              "rightValue": ""
            }
          ]
        }
      },
      "typeVersion": 2
    },
    {
      "id": "4ad898e5-57ae-42ac-bd57-2be23a00c01c",
      "name": "OpenAI Trivia",
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
      "position": [
        -3568,
        1152
      ],
      "parameters": {
        "model": "gpt-4o-mini",
        "options": {
          "maxTokens": 300,
          "temperature": 0.8
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "7e3a5437-14ef-499f-b25d-3cf7b982a74f",
      "name": "Generate Space Trivia",
      "type": "@n8n/n8n-nodes-langchain.chainLlm",
      "position": [
        -3568,
        1008
      ],
      "parameters": {
        "text": "=Generate a fascinating space trivia fact in Japanese about one of these topics (pick randomly):\n- The International Space Station (ISS)\n- Chinese Space Station Tiangong\n- Astronaut life in space\n- Space photography\n- Satellite tracking\n- Space station history\n\nCurrent context:\n- ISS is currently {{ $json.nearbySatellites[0]?.distance || 'far' }} km away\n- There are {{ $json.astronauts.totalInSpace }} people in space right now\n- Weather: {{ $json.weather.description }}\n\nRespond with ONLY a JSON object:\n{\n  \"trivia_jp\": \"[Interesting fact in Japanese, 2-3 sentences]\",\n  \"trivia_en\": \"[Same fact in English]\",\n  \"category\": \"[ISS/Tiangong/Astronauts/Photography/History]\",\n  \"emoji\": \"[Relevant emoji]\"\n}",
        "promptType": "define"
      },
      "typeVersion": 1.4
    },
    {
      "id": "c527ccdc-16f6-44c2-8c71-36f276fdb27c",
      "name": "Format Rich Notification",
      "type": "n8n-nodes-base.code",
      "position": [
        -3200,
        1008
      ],
      "parameters": {
        "jsCode": "const mainData = $('Analyze Observation Conditions1').first().json;\nconst triviaResponse = $input.first().json.text || $input.first().json.output;\n\nlet trivia = {};\ntry {\n  const jsonMatch = triviaResponse.match(/\\{[\\s\\S]*\\}/);\n  if (jsonMatch) trivia = JSON.parse(jsonMatch[0]);\n} catch (e) {\n  trivia = {\n    trivia_jp: 'ISS\u306f\u5730\u7403\u3092\u7d0490\u5206\u30671\u5468\u3057\u30011\u65e5\u306b16\u56de\u306e\u65e5\u306e\u51fa\u3092\u898b\u308b\u3053\u3068\u304c\u3067\u304d\u307e\u3059\u3002',\n    trivia_en: 'The ISS orbits Earth in about 90 minutes, allowing astronauts to see 16 sunrises per day.',\n    category: 'ISS',\n    emoji: '\ud83c\udf05'\n  };\n}\n\n// Build notification message\nconst sat = mainData.nearbySatellites[0];\nconst loc = mainData.userLocation;\n\nlet message = `\ud83d\udef0\ufe0f **\u5b87\u5b99\u30b9\u30c6\u30fc\u30b7\u30e7\u30f3\u63a5\u8fd1\u30a2\u30e9\u30fc\u30c8**\\n\\n`;\nmessage += `\ud83d\udccd **\u89b3\u6e2c\u5730:** ${loc.name}\\n`;\nmessage += `\u23f0 **\u691c\u51fa\u6642\u523b:** ${mainData.timestampJP}\\n\\n`;\n\nmessage += `\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\\n`;\nmessage += `\ud83c\udfaf **\u63a5\u8fd1\u4e2d\u306e\u885b\u661f**\\n`;\nmessage += `\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\\n\\n`;\n\nmainData.nearbySatellites.forEach(s => {\n  message += `**${s.name}**\\n`;\n  message += `  \ud83d\udccf \u8ddd\u96e2: ${s.distance.toLocaleString()} km\\n`;\n  message += `  \ud83e\udded \u65b9\u89d2: ${s.directionJP} (${s.bearing}\u00b0)\\n`;\n  message += `  \ud83d\udcd0 \u4ef0\u89d2: ${s.elevation}\u00b0\\n`;\n  message += `  \ud83d\ude80 \u9ad8\u5ea6: ${s.altitude} km\\n\\n`;\n});\n\nmessage += `\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\\n`;\nmessage += `${mainData.observation.recommendation} **\u89b3\u6e2c\u6761\u4ef6**\\n`;\nmessage += `\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\\n\\n`;\nmessage += `  \ud83d\udcca \u30b9\u30b3\u30a2: ${mainData.observation.score}/100\\n`;\nmessage += `  ${mainData.observation.timeCondition}\\n`;\nmessage += `  ${mainData.observation.conditions.join(', ')}\\n`;\nmessage += `  \ud83c\udf21\ufe0f \u6c17\u6e29: ${mainData.weather.temperature}\u00b0C\\n`;\nmessage += `  \u2601\ufe0f \u96f2\u91cf: ${mainData.weather.cloudiness}%\\n\\n`;\n\nif (mainData.photography.iso) {\n  message += `\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\\n`;\n  message += `\ud83d\udcf8 **\u64ae\u5f71\u8a2d\u5b9a\u30ac\u30a4\u30c9**\\n`;\n  message += `\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\\n\\n`;\n  message += `  ISO: ${mainData.photography.iso}\\n`;\n  message += `  \u7d5e\u308a: ${mainData.photography.aperture}\\n`;\n  message += `  \u30b7\u30e3\u30c3\u30bf\u30fc: ${mainData.photography.shutter}\\n`;\n  message += `  \ud83d\udca1 ${mainData.photography.tips}\\n\\n`;\n}\n\nmessage += `\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\\n`;\nmessage += `\ud83d\udc68\u200d\ud83d\ude80 **\u73fe\u5728\u5b87\u5b99\u306b\u3044\u308b\u4eba\u3005**\\n`;\nmessage += `\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\\n\\n`;\nmessage += `  \ud83c\udf0d \u5408\u8a08: ${mainData.astronauts.totalInSpace}\u4eba\\n`;\nif (mainData.astronauts.iss.length > 0) {\n  message += `  \ud83c\uddfa\ud83c\uddf8 ISS: ${mainData.astronauts.iss.map(a => a.name).join(', ')}\\n`;\n}\nif (mainData.astronauts.tiangong.length > 0) {\n  message += `  \ud83c\udde8\ud83c\uddf3 \u5929\u5bae: ${mainData.astronauts.tiangong.map(a => a.name).join(', ')}\\n`;\n}\n\nmessage += `\\n\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\\n`;\nmessage += `${trivia.emoji} **\u4eca\u65e5\u306e\u5b87\u5b99\u30c8\u30ea\u30d3\u30a2**\\n`;\nmessage += `\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\\n\\n`;\nmessage += `${trivia.trivia_jp}\\n\\n`;\n\nmessage += `\ud83c\udf0d **3D\u30de\u30c3\u30d7\u3067\u78ba\u8a8d:**\\n`;\nmessage += `https://earth.google.com/web/search/${sat.latitude},${sat.longitude}`;\n\nreturn [{\n  json: {\n    ...mainData,\n    trivia,\n    notificationMessage: message,\n    shortMessage: `\ud83d\udef0\ufe0f ${sat.name}\u63a5\u8fd1\u4e2d\uff01 ${sat.directionJP}\u65b9\u5411 ${sat.distance}km`,\n    alertType: 'satellite_overhead'\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "b2df9e60-6761-49f5-b3a0-0e08c8eb51c9",
      "name": "Log to History",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        -2912,
        1008
      ],
      "parameters": {
        "columns": {
          "value": {
            "weather": "={{ $json.weather.description }}",
            "location": "={{ $json.userLocation.name }}",
            "direction": "={{ $json.nearbySatellites[0]?.directionJP }}",
            "elevation": "={{ $json.nearbySatellites[0]?.elevation }}",
            "satellite": "={{ $json.nearbySatellites[0]?.name }}",
            "timestamp": "={{ $json.timestamp }}",
            "cloudiness": "={{ $json.weather.cloudiness }}",
            "distance_km": "={{ $json.nearbySatellites[0]?.distance }}",
            "could_observe": "={{ $json.observation.canObserve }}",
            "trivia_category": "={{ $json.trivia.category }}",
            "observation_score": "={{ $json.observation.score }}"
          },
          "mappingMode": "defineBelow"
        },
        "options": {},
        "operation": "append",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "Sightings",
          "cachedResultUrl": "",
          "cachedResultName": ""
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "={{ $vars.GOOGLE_SHEET_ID }}",
          "cachedResultUrl": "",
          "cachedResultName": ""
        }
      },
      "typeVersion": 4.5
    },
    {
      "id": "f68e0852-c8ac-4c14-8bb4-1c66a8809270",
      "name": "Get ISS Pass Predictions",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -5248,
        1744
      ],
      "parameters": {
        "url": "=https://api.n2yo.com/rest/v1/satellite/visualpasses/25544/{{ $vars.USER_LAT }}/{{ $vars.USER_LON }}/0/7/300/?apiKey={{ $vars.N2YO_API_KEY }}",
        "options": {}
      },
      "typeVersion": 4.2
    },
    {
      "id": "af1c999e-c16d-4f78-9f54-79755fab653e",
      "name": "Get Tiangong Predictions",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -5248,
        1904
      ],
      "parameters": {
        "url": "=https://api.n2yo.com/rest/v1/satellite/visualpasses/48274/{{ $vars.USER_LAT }}/{{ $vars.USER_LON }}/0/7/300/?apiKey={{ $vars.N2YO_API_KEY }}",
        "options": {}
      },
      "typeVersion": 4.2
    },
    {
      "id": "c5ec4863-b61b-40b9-8206-11a985a848bc",
      "name": "Process All Predictions",
      "type": "n8n-nodes-base.code",
      "position": [
        -5008,
        1824
      ],
      "parameters": {
        "jsCode": "const issPasses = $('Get ISS Pass Predictions').first().json.passes || [];\nconst tiangongPasses = $('Get Tiangong Predictions').first().json.passes || [];\n\nfunction formatPass(pass, satName) {\n  const startTime = new Date(pass.startUTC * 1000);\n  const endTime = new Date(pass.endUTC * 1000);\n  const duration = Math.round((pass.endUTC - pass.startUTC) / 60);\n  \n  return {\n    satellite: satName,\n    startTime: startTime.toISOString(),\n    startTimeJP: startTime.toLocaleString('ja-JP', { timeZone: 'Asia/Tokyo' }),\n    endTime: endTime.toISOString(),\n    duration: duration,\n    maxElevation: pass.maxEl,\n    startAzimuth: pass.startAz,\n    startDirection: getDirection(pass.startAz),\n    endAzimuth: pass.endAz,\n    endDirection: getDirection(pass.endAz),\n    magnitude: pass.mag || 'N/A',\n    brightness: getBrightness(pass.mag)\n  };\n}\n\nfunction getDirection(az) {\n  const dirs = ['\u5317', '\u5317\u6771', '\u6771', '\u5357\u6771', '\u5357', '\u5357\u897f', '\u897f', '\u5317\u897f'];\n  return dirs[Math.round(az / 45) % 8];\n}\n\nfunction getBrightness(mag) {\n  if (mag === undefined || mag === 'N/A') return '\u660e\u308b\u3055\u4e0d\u660e';\n  if (mag < -3) return '\u2b50\u2b50\u2b50 \u975e\u5e38\u306b\u660e\u308b\u3044';\n  if (mag < -1) return '\u2b50\u2b50 \u660e\u308b\u3044';\n  if (mag < 1) return '\u2b50 \u898b\u3048\u308b';\n  return '\u6697\u3044\uff08\u53cc\u773c\u93e1\u63a8\u5968\uff09';\n}\n\nconst allPasses = [\n  ...issPasses.map(p => formatPass(p, 'ISS')),\n  ...tiangongPasses.map(p => formatPass(p, 'Tiangong'))\n].sort((a, b) => new Date(a.startTime) - new Date(b.startTime));\n\nconst next7Days = allPasses.slice(0, 14);\n\n// Find best passes (high elevation, good brightness)\nconst bestPasses = allPasses\n  .filter(p => p.maxElevation >= 40)\n  .slice(0, 5);\n\nreturn [{\n  json: {\n    predictions: next7Days,\n    bestPasses,\n    totalPasses: allPasses.length,\n    userLocation: $vars.USER_LOCATION_NAME || 'Tokyo',\n    generatedAt: new Date().toISOString()\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "37df7e2d-dc81-4129-9494-64929a25b4a1",
      "name": "Format Prediction Report",
      "type": "n8n-nodes-base.code",
      "position": [
        -4768,
        1824
      ],
      "parameters": {
        "jsCode": "const data = $input.first().json;\n\nlet message = `\ud83d\uddd3\ufe0f **\u4eca\u9031\u306e\u5b87\u5b99\u30b9\u30c6\u30fc\u30b7\u30e7\u30f3\u53ef\u8996\u4e88\u5831**\\n`;\nmessage += `\ud83d\udccd \u89b3\u6e2c\u5730: ${data.userLocation}\\n`;\nmessage += `\ud83d\udcc5 \u751f\u6210\u65e5\u6642: ${new Date().toLocaleString('ja-JP', { timeZone: 'Asia/Tokyo' })}\\n\\n`;\n\nif (data.bestPasses.length > 0) {\n  message += `\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\\n`;\n  message += `\u2b50 **\u304a\u3059\u3059\u3081\u89b3\u6e2c\u6a5f\u4f1a TOP ${data.bestPasses.length}**\\n`;\n  message += `\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\\n\\n`;\n  \n  data.bestPasses.forEach((pass, i) => {\n    message += `**${i + 1}. ${pass.satellite}**\\n`;\n    message += `  \ud83d\udcc5 ${pass.startTimeJP}\\n`;\n    message += `  \u23f1\ufe0f ${pass.duration}\u5206\u9593\\n`;\n    message += `  \ud83d\udcd0 \u6700\u5927\u4ef0\u89d2: ${pass.maxElevation}\u00b0\\n`;\n    message += `  \ud83e\udded ${pass.startDirection} \u2192 ${pass.endDirection}\\n`;\n    message += `  ${pass.brightness}\\n\\n`;\n  });\n}\n\nmessage += `\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\\n`;\nmessage += `\ud83d\udccb **\u5168\u4e88\u5831 (\u6b21\u306e7\u65e5\u9593)**\\n`;\nmessage += `\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\\n\\n`;\n\ndata.predictions.slice(0, 10).forEach(pass => {\n  const icon = pass.satellite === 'ISS' ? '\ud83c\uddfa\ud83c\uddf8' : '\ud83c\udde8\ud83c\uddf3';\n  message += `${icon} **${pass.startTimeJP}**\\n`;\n  message += `   ${pass.satellite} | ${pass.duration}\u5206 | \u4ef0\u89d2${pass.maxElevation}\u00b0 | ${pass.startDirection}\u2192${pass.endDirection}\\n\\n`;\n});\n\nif (data.predictions.length > 10) {\n  message += `... \u4ed6 ${data.predictions.length - 10} \u4ef6\u306e\u901a\u904e\u304c\u3042\u308a\u307e\u3059\\n\\n`;\n}\n\nmessage += `\ud83d\udca1 **\u89b3\u6e2c\u306e\u30b3\u30c4:**\\n`;\nmessage += `\u2022 \u4ef0\u89d240\u00b0\u4ee5\u4e0a\u304c\u6700\u3082\u898b\u3084\u3059\u3044\\n`;\nmessage += `\u2022 \u8584\u660e\u6642\uff08\u65e5\u6ca1\u5f8c/\u65e5\u51fa\u524d\uff09\u304c\u30d9\u30b9\u30c8\\n`;\nmessage += `\u2022 \u660e\u308b\u3044\u70b9\u304c\u4e00\u5b9a\u901f\u5ea6\u3067\u79fb\u52d5\u3059\u308b\u306e\u3092\u63a2\u3059\\n`;\n\nreturn [{\n  json: {\n    ...data,\n    predictionMessage: message,\n    calendarEvents: data.bestPasses.map(p => ({\n      title: `\ud83d\udef0\ufe0f ${p.satellite} \u89b3\u6e2c\u30c1\u30e3\u30f3\u30b9`,\n      start: p.startTime,\n      end: p.endTime,\n      description: `\u65b9\u89d2: ${p.startDirection}\u2192${p.endDirection}, \u4ef0\u89d2: ${p.maxElevation}\u00b0`\n    }))\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "89e0d973-1b17-40b2-994b-8b54bd0236a6",
      "name": "Send Predictions to Telegram",
      "type": "n8n-nodes-base.telegram",
      "position": [
        -4528,
        1744
      ],
      "parameters": {
        "text": "={{ $json.predictionMessage }}",
        "chatId": "={{ $vars.TELEGRAM_CHAT_ID }}",
        "additionalFields": {
          "parse_mode": "Markdown"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "3db268d7-162f-4917-a77f-6528f3616b8a",
      "name": "Send Predictions to Discord",
      "type": "n8n-nodes-base.discord",
      "position": [
        -4528,
        1904
      ],
      "parameters": {
        "content": "={{ $json.predictionMessage }}",
        "options": {},
        "authentication": "webhook"
      },
      "typeVersion": 2
    },
    {
      "id": "52dc4e6e-b24f-4ea0-b021-cd8a5c4c072d",
      "name": "Loop Calendar Events",
      "type": "n8n-nodes-base.splitInBatches",
      "position": [
        -4288,
        1824
      ],
      "parameters": {
        "options": {
          "reset": false
        }
      },
      "typeVersion": 3
    },
    {
      "id": "99a045a7-686c-4483-9b32-b476c0d817e5",
      "name": "Add to Google Calendar",
      "type": "n8n-nodes-base.googleCalendar",
      "position": [
        -4032,
        1808
      ],
      "parameters": {
        "end": "={{ $json.end }}",
        "start": "={{ $json.start }}",
        "calendar": {
          "__rl": true,
          "mode": "list",
          "value": "primary"
        },
        "additionalFields": {
          "summary": "={{ $json.title }}",
          "showMeAs": "free",
          "description": "={{ $json.description }}"
        }
      },
      "typeVersion": 1.3
    },
    {
      "id": "b8a703ad-2c5f-4ddd-b964-e211a8aec47c",
      "name": "Get History Data",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        -5232,
        2368
      ],
      "parameters": {
        "options": {},
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "Sightings",
          "cachedResultUrl": "",
          "cachedResultName": ""
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "={{ $vars.GOOGLE_SHEET_ID }}",
          "cachedResultUrl": "",
          "cachedResultName": ""
        }
      },
      "typeVersion": 4.5
    },
    {
      "id": "55d4f0c8-1e48-4364-b554-f66bb678e8b5",
      "name": "Calculate Weekly Statistics",
      "type": "n8n-nodes-base.code",
      "position": [
        -4992,
        2368
      ],
      "parameters": {
        "jsCode": "const items = $input.all();\nconst now = new Date();\nconst weekAgo = new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000);\n\nconst weeklyData = items.filter(item => {\n  const ts = new Date(item.json.timestamp);\n  return ts >= weekAgo;\n});\n\nconst stats = {\n  totalSightings: weeklyData.length,\n  observableSightings: weeklyData.filter(d => d.json.could_observe === 'true').length,\n  avgScore: 0,\n  satelliteBreakdown: {},\n  directionBreakdown: {},\n  weatherBreakdown: {},\n  bestSighting: null,\n  triviaCategories: {}\n};\n\nlet totalScore = 0;\nlet bestScore = 0;\n\nweeklyData.forEach(item => {\n  const d = item.json;\n  const score = parseInt(d.observation_score) || 0;\n  totalScore += score;\n  \n  if (score > bestScore) {\n    bestScore = score;\n    stats.bestSighting = d;\n  }\n  \n  // Satellite breakdown\n  const sat = d.satellite || 'Unknown';\n  stats.satelliteBreakdown[sat] = (stats.satelliteBreakdown[sat] || 0) + 1;\n  \n  // Direction breakdown\n  const dir = d.direction || 'Unknown';\n  stats.directionBreakdown[dir] = (stats.directionBreakdown[dir] || 0) + 1;\n  \n  // Weather breakdown\n  const weather = d.weather || 'Unknown';\n  stats.weatherBreakdown[weather] = (stats.weatherBreakdown[weather] || 0) + 1;\n  \n  // Trivia categories\n  const trivia = d.trivia_category || 'General';\n  stats.triviaCategories[trivia] = (stats.triviaCategories[trivia] || 0) + 1;\n});\n\nstats.avgScore = weeklyData.length > 0 ? Math.round(totalScore / weeklyData.length) : 0;\nstats.successRate = weeklyData.length > 0 ? Math.round((stats.observableSightings / weeklyData.length) * 100) : 0;\n\nreturn [{ json: stats }];"
      },
      "typeVersion": 2
    },
    {
      "id": "d434026e-677f-4e60-a804-645a578a0872",
      "name": "OpenAI Report",
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
      "position": [
        -4688,
        2544
      ],
      "parameters": {
        "model": "gpt-4o-mini",
        "options": {
          "maxTokens": 800,
          "temperature": 0.7
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "1d32083b-1a46-47c1-82c4-8662f5d9c0fd",
      "name": "Generate Weekly Report",
      "type": "@n8n/n8n-nodes-langchain.chainLlm",
      "position": [
        -4688,
        2368
      ],
      "parameters": {
        "text": "=You are a space observation analyst. Generate an engaging weekly report in Japanese based on this data:\n\n\ud83d\udcca **Weekly Statistics:**\n- Total satellite passes detected: {{ $json.totalSightings }}\n- Observable passes: {{ $json.observableSightings }}\n- Success rate: {{ $json.successRate }}%\n- Average observation score: {{ $json.avgScore }}/100\n\n\ud83d\udef0\ufe0f **Satellite Breakdown:**\n{{ Object.entries($json.satelliteBreakdown).map(([k, v]) => `- ${k}: ${v} passes`).join('\\n') }}\n\n\ud83e\udded **Most Common Directions:**\n{{ Object.entries($json.directionBreakdown).sort((a,b) => b[1]-a[1]).slice(0,3).map(([k, v]) => `- ${k}: ${v} times`).join('\\n') }}\n\n\ud83c\udf24\ufe0f **Weather Distribution:**\n{{ Object.entries($json.weatherBreakdown).map(([k, v]) => `- ${k}: ${v} times`).join('\\n') }}\n\nGenerate a report in Japanese with:\n1. \ud83d\udcc8 \u9031\u9593\u30cf\u30a4\u30e9\u30a4\u30c8 (Key achievements and interesting observations)\n2. \ud83c\udfaf \u89b3\u6e2c\u6210\u529f\u7387\u5206\u6790 (Analysis of success rate)\n3. \ud83c\udf1f \u6765\u9031\u306e\u5c55\u671b (Outlook for next week)\n4. \ud83d\udca1 \u89b3\u6e2c\u8005\u3078\u306e\u30a2\u30c9\u30d0\u30a4\u30b9 (Tips for improving observations)\n\nKeep it engaging and educational!",
        "promptType": "define"
      },
      "typeVersion": 1.4
    },
    {
      "id": "d2ea8685-8f71-464e-be67-39721ab1cfbc",
      "name": "Format Weekly Report",
      "type": "n8n-nodes-base.code",
      "position": [
        -4272,
        2368
      ],
      "parameters": {
        "jsCode": "const stats = $('Calculate Weekly Statistics').first().json;\nconst aiReport = $input.first().json.text || $input.first().json.output;\n\nlet message = `\ud83d\udcca **\u9031\u9593\u5b87\u5b99\u30b9\u30c6\u30fc\u30b7\u30e7\u30f3\u89b3\u6e2c\u30ec\u30dd\u30fc\u30c8**\\n`;\nmessage += `\ud83d\udcc5 ${new Date().toLocaleDateString('ja-JP')} \u6642\u70b9\\n\\n`;\n\nmessage += `\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\\n`;\nmessage += `\ud83d\udcc8 **\u7d71\u8a08\u30b5\u30de\u30ea\u30fc**\\n`;\nmessage += `\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\\n\\n`;\nmessage += `\ud83d\udef0\ufe0f \u691c\u51fa\u3055\u308c\u305f\u30d1\u30b9: ${stats.totalSightings}\u56de\\n`;\nmessage += `\ud83d\udc40 \u89b3\u6e2c\u53ef\u80fd\u3060\u3063\u305f\u30d1\u30b9: ${stats.observableSightings}\u56de\\n`;\nmessage += `\u2705 \u6210\u529f\u7387: ${stats.successRate}%\\n`;\nmessage += `\u2b50 \u5e73\u5747\u30b9\u30b3\u30a2: ${stats.avgScore}/100\\n\\n`;\n\nif (stats.bestSighting) {\n  message += `\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\\n`;\n  message += `\ud83c\udfc6 **\u4eca\u9031\u306e\u30d9\u30b9\u30c8\u30d1\u30b9**\\n`;\n  message += `\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\\n\\n`;\n  message += `\ud83d\udef0\ufe0f ${stats.bestSighting.satellite}\\n`;\n  message += `\ud83d\udccf ${stats.bestSighting.distance_km}km\\n`;\n  message += `\ud83d\udcca \u30b9\u30b3\u30a2: ${stats.bestSighting.observation_score}\\n\\n`;\n}\n\nmessage += `\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\\n`;\nmessage += `\ud83e\udd16 **AI\u5206\u6790\u30ec\u30dd\u30fc\u30c8**\\n`;\nmessage += `\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\\n\\n`;\nmessage += aiReport;\n\nreturn [{\n  json: {\n    stats,\n    weeklyReport: message\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "aa4dfcdb-55f8-4080-87a3-e8a213b60d23",
      "name": "Send Weekly to Telegram",
      "type": "n8n-nodes-base.telegram",
      "position": [
        -4032,
        2288
      ],
      "parameters": {
        "text": "={{ $json.weeklyReport }}",
        "chatId": "={{ $vars.TELEGRAM_CHAT_ID }}",
        "additionalFields": {
          "parse_mode": "Markdown"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "adadbd20-23b0-4af3-998e-866dbccbb11d",
      "name": "Send Weekly to Discord",
      "type": "n8n-nodes-base.discord",
      "position": [
        -4032,
        2448
      ],
      "parameters": {
        "content": "={{ $json.weeklyReport }}",
        "options": {},
        "authentication": "webhook"
      },
      "typeVersion": 2
    },
    {
      "id": "e9899310-c455-42cb-9184-f59ec3897735",
      "name": "Real-time Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -5536,
        640
      ],
      "parameters": {
        "color": 7,
        "width": 3128,
        "height": 848,
        "content": "### \ud83d\udd04 Real-time Tracking Flow\nEvery 5 minutes: Check satellite positions \u2192 Weather check \u2192 AI trivia \u2192 Log & Notify"
      },
      "typeVersion": 1
    },
    {
      "id": "d6e8ba35-50d7-457e-bc9a-0ab7d973dfb3",
      "name": "Prediction Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -5536,
        1488
      ],
      "parameters": {
        "color": 5,
        "width": 1784,
        "height": 608,
        "content": "### \ud83d\udd2e Daily Predictions Flow\nEvery morning at 6 AM: Fetch 7-day predictions \u2192 Format report \u2192 Add to Google Calendar"
      },
      "typeVersion": 1
    },
    {
      "id": "2757afc4-5dcf-4abe-8e12-299cf5aa3d86",
      "name": "Weekly Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -5536,
        2096
      ],
      "parameters": {
        "color": 4,
        "width": 1752,
        "height": 560,
        "content": "### \ud83d\udcca Weekly Analytics Flow\nEvery Sunday: Aggregate history data \u2192 AI analysis \u2192 Generate insights report"
      },
      "typeVersion": 1
    },
    {
      "id": "3d4ca261-1ee8-40ef-a21a-0023663dc39b",
      "name": "Get ISS Position1",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -5232,
        944
      ],
      "parameters": {
        "url": "http://api.open-notify.org/iss-now.json",
        "options": {}
      },
      "typeVersion": 4.2
    },
    {
      "id": "fb5ec1f9-c4af-4f49-aa3d-33c1412a5859",
      "name": "Analyze Observation Conditions1",
      "type": "n8n-nodes-base.code",
      "position": [
        -4032,
        1024
      ],
      "parameters": {
        "jsCode": "const satelliteData = $('Calculate All Satellites').first().json;\nconst weatherData = $('Get Weather Conditions').first().json;\nconst astronautData = $('Get Astronauts in Space').first().json;\n\n// Weather analysis\nconst weatherMain = weatherData.weather?.[0]?.main || 'Clear';\nconst cloudiness = weatherData.clouds?.all || 0;\nconst visibility = weatherData.visibility || 10000;\nconst sunset = new Date(weatherData.sys?.sunset * 1000);\nconst sunrise = new Date(weatherData.sys?.sunrise * 1000);\nconst now = new Date();\n\n// Observation conditions\nlet observationScore = 100;\nlet conditions = [];\n\nif (['Rain', 'Snow', 'Thunderstorm'].includes(weatherMain)) {\n  observationScore -= 80;\n  conditions.push('\u274c \u60aa\u5929\u5019');\n} else if (cloudiness > 80) {\n  observationScore -= 60;\n  conditions.push('\u2601\ufe0f \u96f2\u91cf\u591a\u3044');\n} else if (cloudiness > 50) {\n  observationScore -= 30;\n  conditions.push('\ud83c\udf25\ufe0f \u90e8\u5206\u7684\u306b\u66c7\u308a');\n} else {\n  conditions.push('\u2728 \u6674\u5929');\n}\n\n// Time of day analysis\nlet timeCondition = '';\nconst hour = now.getHours();\nif (hour >= 18 && hour < 21 || hour >= 5 && hour < 7) {\n  timeCondition = '\ud83c\udf05 \u8584\u660e\u6642\u9593\u5e2f - \u6700\u9ad8\u306e\u89b3\u6e2c\u6761\u4ef6\uff01';\n  observationScore += 20;\n} else if (hour >= 21 || hour < 5) {\n  timeCondition = '\ud83c\udf19 \u591c\u9593 - \u826f\u597d\u306a\u89b3\u6e2c\u6761\u4ef6';\n} else {\n  timeCondition = '\u2600\ufe0f \u65e5\u4e2d - \u89b3\u6e2c\u56f0\u96e3';\n  observationScore -= 40;\n}\n\nobservationScore = Math.max(0, Math.min(100, observationScore));\n\n// Astronaut info\nconst issAstronauts = astronautData.people?.filter(p => p.craft === 'ISS') || [];\nconst tiangongAstronauts = astronautData.people?.filter(p => p.craft === 'Tiangong') || [];\n\n// Photography recommendations\nlet photoSettings = {};\nif (observationScore >= 60) {\n  if (hour >= 18 || hour < 6) {\n    photoSettings = {\n      iso: '1600-3200',\n      aperture: 'f/2.8-4',\n      shutter: '1/250-1/500\u79d2',\n      tips: '\u4e09\u811a\u4f7f\u7528\u63a8\u5968\u3002\u8ffd\u5c3e\u64ae\u5f71\u304c\u30d9\u30b9\u30c8\u3002'\n    };\n  } else {\n    photoSettings = {\n      iso: '100-400',\n      aperture: 'f/8-11',\n      shutter: '1/1000\u79d2\u4ee5\u4e0a',\n      tips: '\u65e5\u4e2d\u306f\u671b\u9060\u93e1+\u30bd\u30fc\u30e9\u30fc\u30d5\u30a3\u30eb\u30bf\u30fc\u63a8\u5968'\n    };\n  }\n}\n\nreturn [{\n  json: {\n    ...satelliteData,\n    weather: {\n      main: weatherMain,\n      description: weatherData.weather?.[0]?.description,\n      cloudiness,\n      visibility,\n      temperature: weatherData.main?.temp,\n      humidity: weatherData.main?.humidity\n    },\n    observation: {\n      score: observationScore,\n      conditions,\n      timeCondition,\n      canObserve: observationScore >= 40,\n      recommendation: observationScore >= 70 ? '\ud83d\udfe2 \u89b3\u6e2c\u63a8\u5968' : observationScore >= 40 ? '\ud83d\udfe1 \u89b3\u6e2c\u53ef\u80fd' : '\ud83d\udd34 \u89b3\u6e2c\u56f0\u96e3'\n    },\n    astronauts: {\n      iss: issAstronauts,\n      tiangong: tiangongAstronauts,\n      totalInSpace: astronautData.number || 0\n    },\n    photography: photoSettings\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "c4758a00-7bc8-43b0-a2c2-906217a806ad",
      "name": "Send to Discord2",
      "type": "n8n-nodes-base.discord",
      "position": [
        -2688,
        1008
      ],
      "parameters": {
        "content": "={{ $json.notificationMessage }}",
        "options": {},
        "authentication": "webhook"
      },
      "typeVersion": 2
    },
    {
      "id": "bc84d059-9b7c-4352-ab05-c510336014d6",
      "name": "Send to Telegram3",
      "type": "n8n-nodes-base.telegram",
      "position": [
        -2688,
        1168
      ],
      "parameters": {
        "text": "={{ $json.notificationMessage }}",
        "chatId": "={{ $vars.TELEGRAM_CHAT_ID }}",
        "additionalFields": {
          "parse_mode": "Markdown"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "3513c339-c26c-4c89-9642-1268911f0ffe",
      "name": "Send to Slack1",
      "type": "n8n-nodes-base.slack",
      "position": [
        -2688,
        1328
      ],
      "parameters": {
        "text": "={{ $json.shortMessage }}",
        "otherOptions": {}
      },
      "typeVersion": 2.2
    },
    {
      "id": "3ad6d550-a78e-428b-af2c-017d7a88f325",
      "name": "Workflow Description1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -6080,
        640
      ],
      "parameters": {
        "width": 520,
        "height": 860,
        "content": "## \ud83d\udef0\ufe0f Space Station Intelligence Hub\n### Advanced Satellite Tracker with Predictions & Analytics\n\n**What this workflow does:**\nTracks the International Space Station and Chinese Tiangong station in real-time, predicts visible passes, provides photography guidance, and generates weekly analytics reports.\n\n**\ud83d\ude80 Key Features:**\n- \ud83d\udce1 Multi-satellite tracking (ISS + Tiangong)\n- \ud83d\udd2e 7-day pass predictions with N2YO API\n- \ud83c\udf24\ufe0f Weather-aware observation scoring\n- \ud83d\udcf8 Camera settings recommendations\n- \ud83d\udc68\u200d\ud83d\ude80 Real-time astronaut information\n- \ud83e\udd16 AI-generated space trivia (GPT-4)\n- \ud83d\udcca Google Sheets history tracking\n- \ud83d\udcc5 Google Calendar integration\n- \ud83d\udcc8 Weekly AI analytics reports\n- \ud83d\udd14 Multi-channel notifications\n\n**\ud83d\udccb Required Services:**\n1. N2YO API (free) - Satellite tracking\n2. OpenWeatherMap API (free) - Weather data\n3. OpenAI API - AI trivia & reports\n4. Google Sheets - History database\n5. Google Calendar - Event scheduling\n6. Discord/Telegram/Slack - Notifications\n\n**\u2699\ufe0f Environment Variables:**\n- USER_LAT, USER_LON, USER_LOCATION_NAME\n- N2YO_API_KEY\n- GOOGLE_SHEET_ID\n- TELEGRAM_CHAT_ID\n- SLACK_CHANNEL\n\n**\ud83d\udcca Google Sheet Columns:**\ntimestamp, satellite, distance_km, direction, elevation, observation_score, weather, cloudiness, could_observe, location, trivia_category"
      },
      "typeVersion": 1
    },
    {
      "id": "79b04fd4-a4ce-4964-bd61-3fce668e6584",
      "name": "Step 1 - Fetch Positions",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -5488,
        800
      ],
      "parameters": {
        "color": 7,
        "width": 280,
        "height": 128,
        "content": "**Step 1: Fetch Satellite Positions**\nGet current positions from:\n- Open Notify API (ISS basic)\n- N2YO API (ISS detailed + Tiangong)"
      },
      "typeVersion": 1
    },
    {
      "id": "f674db6d-71e1-4bcb-9de2-bb8aa843118e",
      "name": "Step 2 - Calculate",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -4848,
        816
      ],
      "parameters": {
        "color": 7,
        "width": 260,
        "content": "**Step 2: Calculate Distance & Direction**\nUsing Haversine formula to compute:\n- Distance from user location\n- Compass bearing (N/NE/E...)\n- Elevation angle"
      },
      "typeVersion": 1
    },
    {
      "id": "a80edf3f-991e-4bcf-98f5-f6bf931f3c14",
      "name": "Step 3 - Filter Nearby",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -4544,
        816
      ],
      "parameters": {
        "color": 7,
        "width": 220,
        "height": 100,
        "content": "**Step 3: Check Proximity**\nFilter satellites within visibility radius (default 800km)"
      },
      "typeVersion": 1
    },
    {
      "id": "13387e14-ac1d-4436-a3ec-9c1c52817e9d",
      "name": "Step 4 - Context",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -4304,
        816
      ],
      "parameters": {
        "color": 7,
        "width": 220,
        "height": 124,
        "content": "**Step 4: Get Context Data**\n- Weather conditions (OpenWeatherMap)\n- Current astronauts in space"
      },
      "typeVersion": 1
    },
    {
      "id": "21842888-97e3-40ac-8d21-3bd4182855f1",
      "name": "Step 5 - Score",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -4000,
        816
      ],
      "parameters": {
        "color": 7,
        "height": 140,
        "content": "**Step 5: Score Observation Conditions**\nCalculate 0-100 score based on:\n- Weather (clouds, rain)\n- Time of day (twilight = best)\n- Generate camera settings"
      },
      "typeVersion": 1
    },
    {
      "id": "4dc59d0f-b40e-4ae0-a42f-7542f83ce7d4",
      "name": "Step 6 - AI Trivia",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -3568,
        816
      ],
      "parameters": {
        "color": 7,
        "height": 100,
        "content": "**Step 6: Generate AI Trivia**\nOpenAI generates fun space facts in Japanese based on current context"
      },
      "typeVersion": 1
    },
    {
      "id": "0d04e8c6-4975-4770-90e6-eb7bcbf03ca3",
      "name": "Step 7 - Format",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -3264,
        784
      ],
      "parameters": {
        "color": 7,
        "width": 220,
        "height": 208,
        "content": "**Step 7: Format Notification**\nBuild rich message with:\n- Satellite details\n- Observation score\n- Camera tips\n- Astronaut list\n- Space trivia"
      },
      "typeVersion": 1
    },
    {
      "id": "50dacd3f-e069-447c-accd-59c6610f017f",
      "name": "Step 8 - Log & Send",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -2992,
        864
      ],
      "parameters": {
        "color": 7,
        "width": 220,
        "height": 116,
        "content": "**Step 8: Log & Notify**\n- Save to Google Sheets\n- Send to Discord/Telegram/Slack"
      },
      "typeVersion": 1
    },
    {
      "id": "d91ea33a-76dc-4d0f-bd90-d59b03383cd1",
      "name": "Step 1 - Fetch Predictions",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -5504,
        1600
      ],
      "parameters": {
        "color": 7,
        "height": 140,
        "content": "**Step 1: Fetch 7-Day Predictions**\nN2YO visual passes API for:\n- ISS (NORAD 25544)\n- Tiangong (NORAD 48274)"
      },
      "typeVersion": 1
    },
    {
      "id": "39c90a07-f964-4c99-9ec9-6218b932f5c6",
      "name": "Step 2 - Process",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -5104,
        1616
      ],
      "parameters": {
        "color": 7,
        "height": 172,
        "content": "**Step 2: Process & Rank Passes**\n- Combine ISS + Tiangong passes\n- Sort by time\n- Find best passes (elevation \u226540\u00b0)"
      },
      "typeVersion": 1
    },
    {
      "id": "6934c72f-9881-4b0e-8ef7-034c8ff4a225",
      "name": "Step 3 - Format Report",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -4816,
        1616
      ],
      "parameters": {
        "color": 7,
        "height": 172,
        "content": "**Step 3: Format Report**\nCreate Japanese report with:\n- Top observation opportunities\n- Full 7-day schedule\n- Viewing tips"
      },
      "typeVersion": 1
    },
    {
      "id": "666c7056-530b-47bf-8c44-ee96a4710261",
      "name": "Step 4 - Notify",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -4544,
        1616
      ],
      "parameters": {
        "color": 7,
        "width": 200,
        "height": 116,
        "content": "**Step 4: Notify Users**\nSend prediction report to:\n- Telegram\n- Discord"
      },
      "typeVersion": 1
    },
    {
      "id": "1e037aed-e7ce-4eae-8b41-1fe8ace8e120",
      "name": "Step 5 - Calendar",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -4176,
        1616
      ],
      "parameters": {
        "color": 7,
        "height": 100,
        "content": "**Step 5: Add to Calendar**\nLoop through best passes and create Google Calendar events"
      },
      "typeVersion": 1
    },
    {
      "id": "a703fc5e-1188-4030-9ab6-e036671aac5c",
      "name": "Step 1 - Load History",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -5264,
        2240
      ],
      "parameters": {
        "color": 7,
        "width": 220,
        "height": 116,
        "content": "**Step 1: Load History Data**\nFetch all sighting records from Google Sheets"
      },
      "typeVersion": 1
    },
    {
      "id": "ce93ac1a-148e-4a87-b768-a6dd9c2c7a16",
      "name": "Step 2 - Stats",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -5024,
        2240
      ],
      "parameters": {
        "color": 7,
        "height": 104,
        "content": "**Step 2: Calculate Statistics**\n- Total/observable sightings\n- Success rate\n- Breakdown by satellite, direction, weather"
      },
      "typeVersion": 1
    },
    {
      "id": "f13aa8da-5fd8-47a4-9972-2026d2cffc53",
      "name": "Step 3 - AI Analysis",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -4720,
        2192
      ],
      "parameters": {
        "color": 7,
        "height": 156,
        "content": "**Step 3: AI Analysis**\nGPT-4 generates insights:\n- Weekly highlights\n- Success rate analysis\n- Next week outlook\n- Observer tips"
      },
      "typeVersion": 1
    },
    {
      "id": "b95009f7-5a81-48b7-8fbe-e67414a035ab",
      "name": "Step 4 - Send Report",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -4352,
        2240
      ],
      "parameters": {
        "color": 7,
        "height": 100,
        "content": "**Step 4: Format & Send Report**\nCombine stats + AI analysis into final report, send to channels"
      },
      "typeVersion": 1
    }
  ],
  "connections": {
    "OpenAI Report": {
      "ai_languageModel": [
        [
          {
            "node": "Generate Weekly Report",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "OpenAI Trivia": {
      "ai_languageModel": [
        [
          {
            "node": "Generate Space Trivia",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Log to History": {
      "main": [
        [
          {
            "node": "Send to Discord2",
            "type": "main",
            "index": 0
          },
          {
            "node": "Send to Telegram3",
            "type": "main",
            "index": 0
          },
          {
            "node": "Send to Slack1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get History Data": {
      "main": [
        [
          {
            "node": "Calculate Weekly Statistics",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get ISS Position1": {
      "main": [
        [
          {
            "node": "Get ISS Detailed (N2YO)",
            "type": "main",
            "index": 0
          },
          {
            "node": "Get Tiangong Position",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Format Weekly Report": {
      "main": [
        [
          {
            "node": "Send Weekly to Telegram",
            "type": "main",
            "index": 0
          },
          {
            "node": "Send Weekly to Discord",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Loop Calendar Events": {
      "main": [
        [
          {
            "node": "Add to Google Calendar",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Any Satellite Nearby?": {
      "main": [
        [
          {
            "node": "Get Weather Conditions",
            "type": "main",
            "index": 0
          },
          {
            "node": "Get Astronauts in Space",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Generate Space Trivia": {
      "main": [
        [
          {
            "node": "Format Rich Notification",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Tiangong Position": {
      "main": [
        [
          {
            "node": "Calculate All Satellites",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Add to Google Calendar": {
      "main": [
        [
          {
            "node": "Loop Calendar Events",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Generate Weekly Report": {
      "main": [
        [
          {
            "node": "Format Weekly Report",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Weather Conditions": {
      "main": [
        [
          {
            "node": "Analyze Observation Conditions1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Observable Conditions?": {
      "main": [
        [
          {
            "node": "Generate Space Trivia",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Astronauts in Space": {
      "main": [
        [
          {
            "node": "Analyze Observation Conditions1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get ISS Detailed (N2YO)": {
      "main": [
        [
          {
            "node": "Calculate All Satellites",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Process All Predictions": {
      "main": [
        [
          {
            "node": "Format Prediction Report",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Real-time Check (5 min)": {
      "main": [
        [
          {
            "node": "Get ISS Position1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Calculate All Satellites": {
      "main": [
        [
          {
            "node": "Any Satellite Nearby?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Daily Predictions (6 AM)": {
      "main": [
        [
          {
            "node": "Get ISS Pass Predictions",
            "type": "main",
            "index": 0
          },
          {
            "node": "Get Tiangong Predictions",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Format Prediction Report": {
      "main": [
        [
          {
            "node": "Send Predictions to Telegram",
            "type": "main",
            "index": 0
          },
          {
            "node": "Send Predictions to Discord",
            "type": "main",
            "index": 0
          },
          {
            "node": "Loop Calendar Events",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Format Rich Notification": {
      "main": [
        [
          {
            "node": "Log to History",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get ISS Pass Predictions": {
      "main": [
        [
          {
            "node": "Process All Predictions",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Tiangong Predictions": {
      "main": [
        [
          {
            "node": "Process All Predictions",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Calculate Weekly Statistics": {
      "main": [
        [
          {
            "node": "Generate Weekly Report",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Weekly Report (Sunday 8 PM)": {
      "main": [
        [
          {
            "node": "Get History Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Analyze Observation Conditions1": {
      "main": [
        [
          {
            "node": "Observable Conditions?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}