AI Newsletter Digest improvements: fixed QP soft line break decoding, URL extraction, and content cleaning
This commit is contained in:
109
skills/openclaw-self-healing/scripts/get-exchange-rate.py
Normal file
109
skills/openclaw-self-healing/scripts/get-exchange-rate.py
Normal file
@@ -0,0 +1,109 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
실시간 환율 조회 (USD/KRW)
|
||||
Provider: exchangerate.host → open.er-api.com (페일오버)
|
||||
캐시: 30분
|
||||
"""
|
||||
|
||||
import json
|
||||
import sys
|
||||
import time
|
||||
from pathlib import Path
|
||||
from urllib.request import Request, urlopen
|
||||
from urllib.error import URLError, HTTPError
|
||||
|
||||
CACHE_FILE = Path.home() / ".openclaw" / "cache" / "exchange-rate.json"
|
||||
CACHE_TTL = 1800 # 30분
|
||||
|
||||
def load_cache():
|
||||
"""캐시 파일 읽기"""
|
||||
if not CACHE_FILE.exists():
|
||||
return None
|
||||
try:
|
||||
with open(CACHE_FILE, 'r') as f:
|
||||
data = json.load(f)
|
||||
# TTL 체크
|
||||
if time.time() - data.get('timestamp', 0) < CACHE_TTL:
|
||||
return data
|
||||
except:
|
||||
pass
|
||||
return None
|
||||
|
||||
def save_cache(rate, source):
|
||||
"""캐시 파일 저장"""
|
||||
CACHE_FILE.parent.mkdir(parents=True, exist_ok=True)
|
||||
with open(CACHE_FILE, 'w') as f:
|
||||
json.dump({
|
||||
'rate': rate,
|
||||
'source': source,
|
||||
'timestamp': time.time()
|
||||
}, f)
|
||||
|
||||
def fetch_exchangerate_host():
|
||||
"""Provider 1: exchangerate.host"""
|
||||
try:
|
||||
url = "https://api.exchangerate.host/latest?base=USD&symbols=KRW"
|
||||
req = Request(url, headers={
|
||||
'Accept': 'application/json',
|
||||
'User-Agent': 'OpenClaw/1.0'
|
||||
})
|
||||
with urlopen(req, timeout=7) as response:
|
||||
if response.status != 200:
|
||||
return None
|
||||
data = json.loads(response.read().decode())
|
||||
return data.get('rates', {}).get('KRW')
|
||||
except (URLError, HTTPError, Exception) as e:
|
||||
print(f"[FX] exchangerate.host error: {e}", file=sys.stderr)
|
||||
return None
|
||||
|
||||
def fetch_er_api():
|
||||
"""Provider 2: open.er-api.com"""
|
||||
try:
|
||||
url = "https://open.er-api.com/v6/latest/USD"
|
||||
req = Request(url, headers={
|
||||
'Accept': 'application/json',
|
||||
'User-Agent': 'OpenClaw/1.0'
|
||||
})
|
||||
with urlopen(req, timeout=7) as response:
|
||||
if response.status != 200:
|
||||
return None
|
||||
data = json.loads(response.read().decode())
|
||||
if data.get('result') != 'success':
|
||||
return None
|
||||
return data.get('rates', {}).get('KRW')
|
||||
except (URLError, HTTPError, Exception) as e:
|
||||
print(f"[FX] open.er-api.com error: {e}", file=sys.stderr)
|
||||
return None
|
||||
|
||||
def get_rate():
|
||||
"""환율 조회 (캐시 → API)"""
|
||||
# 1. 캐시 확인
|
||||
cached = load_cache()
|
||||
if cached:
|
||||
return cached['rate'], cached['source'], 'cached'
|
||||
|
||||
# 2. API 호출 (페일오버)
|
||||
rate = fetch_exchangerate_host()
|
||||
source = 'exchangerate.host'
|
||||
if rate is None:
|
||||
rate = fetch_er_api()
|
||||
source = 'open.er-api.com'
|
||||
|
||||
if rate is None:
|
||||
# 3. 마지막 캐시라도 있으면 사용
|
||||
if cached:
|
||||
return cached['rate'], cached['source'], 'stale-cache'
|
||||
raise Exception("환율 조회 실패: USD→KRW")
|
||||
|
||||
# 4. 캐시 저장
|
||||
save_cache(rate, source)
|
||||
return rate, source, 'fresh'
|
||||
|
||||
if __name__ == '__main__':
|
||||
try:
|
||||
rate, source, status = get_rate()
|
||||
print(f"{rate:.2f}") # 숫자만 출력 (스크립트 연동용)
|
||||
print(f"# Source: {source} ({status})", file=sys.stderr)
|
||||
except Exception as e:
|
||||
print(f"ERROR: {e}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
Reference in New Issue
Block a user