#!/bin/bash # 和风天气查询脚本 # 读取 stdin JSON: {"location":"北京","days":3} read -r input LOCATION=$(echo "$input" | python3 -c " import sys, json d = json.load(sys.stdin) print(d.get('location', '')) " 2>/dev/null) DAYS=$(echo "$input" | python3 -c " import sys, json d = json.load(sys.stdin) print(d.get('days', 3)) " 2>/dev/null) if [ -z "$LOCATION" ]; then echo "请提供 location 参数" exit 1 fi # 从环境变量读取配置 API_HOST="${QWEATHER_API_HOST}" KEY_ID="${QWEATHER_JWT_KEY_ID}" PROJECT_ID="${QWEATHER_JWT_PROJECT_ID}" KEY_FILE="${QWEATHER_JWT_PRIVATE_KEY_FILE:-qweather/ed25519-private.pem}" TTL="${QWEATHER_JWT_TTL_SECONDS:-3600}" if [ -z "$API_HOST" ]; then echo "请设置 QWEATHER_API_HOST 环境变量" exit 1 fi # 生成 JWT(使用 python3) JWT=$(python3 -c " import json, time, base64, struct, sys from pathlib import Path key_id = '$KEY_ID' project_id = '$PROJECT_ID' key_file = '$KEY_FILE' ttl = int('$TTL') if not key_id or not project_id: sys.exit(0) # Read Ed25519 private key key_path = Path(key_file) if not key_path.exists(): sys.exit(0) key_data = key_path.read_text() # Simple base64url def b64url(data): return base64.urlsafe_b64encode(data).rstrip(b'=').decode() # Extract raw key bytes from PEM import re pem_match = re.search(r'-----BEGIN PRIVATE KEY-----\\s*(.*?)\\s*-----END PRIVATE KEY-----', key_data, re.DOTALL) if not pem_match: sys.exit(0) der = base64.b64decode(pem_match.group(1)) # PKCS8 Ed25519 private key: last 32 bytes are the seed seed = der[-32:] from hashlib import sha512 h = sha512(seed).digest() a = bytearray(h[:32]) a[0] &= 248 a[31] &= 127 a[31] |= 64 # Sign using RFC 8032 def ed25519_sign(message, seed_bytes): h = sha512(seed_bytes).digest() a = bytearray(h[:32]) a[0] &= 248 a[31] &= 127 a[31] |= 64 r = sha512(bytes(a[32:]) + message).digest() R = _sc_reduce(r) R_bytes = _sc_mul_base(R) S = _sc_add(R, _sc_mul(_sc_reduce(sha512(bytes(R_bytes) + bytes(a) + message).digest()), _sc_reduce(bytes(a)))) return bytes(R_bytes) + _sc_encode(S) # Skipping full impl - use openssl instead print('') " 2>/dev/null) # If python3 JWT failed, try openssl if [ -z "$JWT" ]; then NOW=$(date +%s) IAT=$((NOW - 30)) EXP=$((NOW + TTL)) HEADER=$(echo -n '{"alg":"EdDSA","kid":"'"$KEY_ID"'"}' | base64 -w0 | tr '+/' '-_' | tr -d '=') PAYLOAD=$(echo -n '{"sub":"'"$PROJECT_ID"'","iat":'"$IAT"',"exp":'"$EXP"'}' | base64 -w0 | tr '+/' '-_' | tr -d '=') SIGNING_INPUT="${HEADER}.${PAYLOAD}" # Try openssl pkeyutl (OpenSSL >= 3.0, needs temp file for -rawin) TMPFILE=$(mktemp) echo -n "$SIGNING_INPUT" > "$TMPFILE" SIGNATURE=$(openssl pkeyutl -sign -inkey "$KEY_FILE" -rawin -in "$TMPFILE" 2>/dev/null | base64 -w0 | tr '+/' '-_' | tr -d '=') || true rm -f "$TMPFILE" if [ -z "$SIGNATURE" ]; then # Fallback: try older openssl SIGNATURE=$(echo -n "$SIGNING_INPUT" | openssl dgst -sign "$KEY_FILE" -keyform PEM -sha512 2>/dev/null | base64 -w0 | tr '+/' '-_' | tr -d '=') || true fi if [ -n "$SIGNATURE" ]; then JWT="${SIGNING_INPUT}.${SIGNATURE}" fi fi # Try env var JWT as fallback if [ -z "$JWT" ]; then JWT="${QWEATHER_JWT}" fi if [ -z "$JWT" ]; then echo "无法生成 QWeather JWT,请检查 QWEATHER_JWT_KEY_ID/QWEATHER_JWT_PROJECT_ID/QWEATHER_JWT_PRIVATE_KEY_FILE 配置" exit 1 fi # 查询城市 ID CITY_RESP=$(curl -s --compressed "https://geoapi.qweather.com/v2/city/lookup?location=$(echo -n "$LOCATION" | python3 -c "import sys,urllib.parse; print(urllib.parse.quote(sys.stdin.read().strip()))")" -H "Authorization: Bearer $JWT") CITY_ID=$(echo "$CITY_RESP" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d['location'][0]['id'] if d.get('location') else '')" 2>/dev/null) if [ -z "$CITY_ID" ]; then echo "未找到城市: $LOCATION" exit 1 fi # 查询实况天气 NOW_RESP=$(curl -s --compressed "https://${API_HOST}/v7/weather/now?location=${CITY_ID}&lang=zh&unit=m" -H "Authorization: Bearer $JWT") NOW_TEMP=$(echo "$NOW_RESP" | python3 -c "import sys,json; d=json.load(sys.stdin); print(f\"{d['now']['temp']}°C, {d['now']['text']}\")" 2>/dev/null) # 查询预报(最多7天) ENDPOINT_DAYS=$DAYS [ "$DAYS" -gt 3 ] && ENDPOINT_DAYS=7 [ "$DAYS" -le 3 ] && ENDPOINT_DAYS=3 DAILY_RESP=$(curl -s --compressed "https://${API_HOST}/v7/weather/${ENDPOINT_DAYS}d?location=${CITY_ID}&lang=zh&unit=m" -H "Authorization: Bearer $JWT") FORECAST=$(echo "$DAILY_RESP" | python3 -c " import sys, json d = json.load(sys.stdin) days = d.get('daily', [])[:int('$DAYS')] lines = ['未来天气:'] for day in days: lines.append(f\" {day['fxDate']}: {day['textDay']} {day['tempMin']}~{day['tempMax']}°C\") print('\\n'.join(lines)) " 2>/dev/null) echo "${LOCATION}天气: 当前 ${NOW_TEMP}" echo "" echo "$FORECAST"