Theowise / apps /saju.py
hoon1018's picture
Update apps/saju.py
dadc3a2 verified
import os
import requests
import re # ์ •๊ทœ์‹ ํŒŒ์‹ฑ์šฉ
import random
from datetime import datetime, timedelta
from typing import Union
from fastapi import APIRouter, Request
from fastapi.responses import JSONResponse, HTMLResponse
from fastapi.templating import Jinja2Templates
from pydantic import BaseModel
from google import genai
router = APIRouter()
templates = Jinja2Templates(directory="templates")
# [์ˆ˜์ • 1] API ํ‚ค ์ €์žฅ ๋ณ€์ˆ˜๋ช… ์ •์˜
GEMINI_API_KEYS = []
default_key = os.environ.get("GEMINI_API_KEY")
if default_key: GEMINI_API_KEYS.append(default_key)
for i in range(1, 11):
key = os.environ.get(f"GEMINI_API_KEY{i}")
if key and key not in GEMINI_API_KEYS: GEMINI_API_KEYS.append(key)
SLACK_WEBHOOK_URL = os.environ.get("SLACK_WEBHOOK_URL")
def send_slack_message(message):
if not SLACK_WEBHOOK_URL: return
try: requests.post(SLACK_WEBHOOK_URL, json={"text": message})
except: pass
class SajuRequest(BaseModel):
year: Union[str, int]
month: Union[str, int]
day: Union[str, int]
time: Union[str, int] = "์‹œ๊ฐ„ ๋ชจ๋ฆ„"
@router.get("/saju", response_class=HTMLResponse)
async def main_saju(request: Request):
return templates.TemplateResponse("main_saju.html", {"request": request})
@router.post("/analyze")
def analyze_saju(req: SajuRequest):
req_year = str(req.year)
req_month = str(req.month)
req_day = str(req.day)
req_time = str(req.time)
candidate_models = [
"gemini-3-flash-preview",
"gemini-3-pro-preview",
"gemini-flash-latest"
]
birth_info = f"{req_year}๋…„ {req_month}์›” {req_day}์ผ ({req_time})"
korea_now = datetime.now() + timedelta(hours=9)
today_date = korea_now.strftime("%Y๋…„ %m์›” %d์ผ")
prompt = f"""
๋‹น์‹ ์€ ํŠธ๋ Œ๋””ํ•œ 'ํผ์Šค๋„ ์‚ฌ์ฃผ ํŒจ์…˜ ๋””๋ ‰ํ„ฐ' Theo์ž…๋‹ˆ๋‹ค.
[์‚ฌ์šฉ์ž ์ •๋ณด]
- ์ƒ๋…„์›”์ผ: {birth_info} (์–‘๋ ฅ/Solar Calendar ๊ธฐ์ค€)
- ์š”์ฒญ์‚ฌํ•ญ: ์œ„ ์–‘๋ ฅ ๋‚ ์งœ๋ฅผ ๋ฐ”ํƒ•์œผ๋กœ ์ •ํ™•ํ•œ ์‚ฌ์ฃผ๋ฅผ ๋ถ„์„ํ•˜์„ธ์š”.
[ํ˜„์žฌ ์‹œ์ ]
- ์˜ค๋Š˜ ๋‚ ์งœ: {today_date}
[์ถœ๋ ฅ ๊ฐ€์ด๋“œ]
1. **์ค‘์š”: ๋ชจ๋“  ๊ฐ•์กฐ(Bold) ์ฒ˜๋ฆฌ๋Š” ๋งˆํฌ๋‹ค์šด(**)์ด ์•„๋‹Œ HTML `<b>` ํƒœ๊ทธ๋ฅผ ์‚ฌ์šฉํ•˜์„ธ์š”.**
2. ์ธ์‚ฌ๋ง: ๋ฐ˜๋“œ์‹œ `<div class="greeting">` ํƒœ๊ทธ๋กœ ๊ฐ์‹ธ์„œ ์ž‘์„ฑํ•˜์„ธ์š”.
3. ์†Œ๊ฐœ ์ƒ๋žต: ์ž๊ธฐ์†Œ๊ฐœ๋Š” ํ•˜์ง€ ๋งˆ์„ธ์š”.
4. ํ˜•์‹: ๋ชจ๋“  ์„น์…˜('์˜ค๋Š˜์˜ ์‚ฌ์ฃผ ๋ถ„์„', '์˜ค๋Š˜์˜ ํ–‰์šด ์ปฌ๋Ÿฌ', '์˜ค๋Š˜์˜ ํ–‰์šด ๋ฒˆํ˜ธ', '์˜ค๋Š˜์˜ ์ถ”์ฒœ ์ฝ”๋””', '์˜ค๋Š˜์˜ ๋งˆ์Œโผผ๊ฐ€์ง')์€ <details><summary>... [๋ณด๊ธฐ]</summary></details> ํƒœ๊ทธ๋กœ ๊ฐ์‹ธ์„œ ์ ‘์–ด๋‘์„ธ์š”.
5. **(ํ•„์ˆ˜)** '์˜ค๋Š˜์˜ ํ–‰์šด ๋ฒˆํ˜ธ' ์„น์…˜์„ ์ถ”๊ฐ€ํ•˜์—ฌ 1๋ถ€ํ„ฐ 45 ์‚ฌ์ด์˜ ๊ฒน์น˜์ง€ ์•Š๋Š” ์ˆซ์ž 6๊ฐœ๋ฅผ ์ถ”์ฒœํ•ด ์ฃผ์„ธ์š”.
6. **(์‹œ์Šคํ…œ ํŒŒ์‹ฑ์šฉ - ๋งค์šฐ ์ค‘์š”)** ๋ฐฑ์—”๋“œ ์‹œ์Šคํ…œ ํŒŒ์‹ฑ์„ ์œ„ํ•ด ์‘๋‹ต ํ…์ŠคํŠธ ๋งจ ๋งˆ์ง€๋ง‰ ์ค„์— ๋ฐ˜๋“œ์‹œ ์•„๋ž˜์™€ ๊ฐ™์€ ํ˜•์‹์˜ HTML ์ฃผ์„์„ ์ถ”๊ฐ€ํ•˜์„ธ์š”. (์ˆซ์ž๋Š” ์‰ผํ‘œ๋กœ ๊ตฌ๋ถ„)
7. ํ†ค์•ค๋งค๋„ˆ: ์ „๋ฌธ์ ์ด๊ณ  ์šฐ์•„ํ•œ ์–ด์กฐ.
"""
last_error = None
if not GEMINI_API_KEYS:
return JSONResponse(content={'result': "<div class='greeting'>์„œ๋ฒ„์— API ํ‚ค๊ฐ€ ์„ค์ •๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค.</div>"})
send_slack_message(f"๐Ÿ”ฎ [Theo] ๋ถ„์„ ์š”์ฒญ: {birth_info}")
for key_idx, current_key in enumerate(GEMINI_API_KEYS):
key_alias = f"KEY_{key_idx+1}"
try:
client = genai.Client(api_key=current_key)
except Exception:
continue
for model_name in candidate_models:
try:
response = client.models.generate_content(
model=model_name,
contents=prompt
)
if response.text:
send_slack_message(f"โœ… [์„ฑ๊ณต] {key_alias} / {model_name}")
# ==========================================
# ๐Ÿšจ [์ถ”๊ฐ€๋œ ๋กœ์ง] ํ–‰์šด ๋ฒˆํ˜ธ ์ถ”์ถœ (ํŒŒ์‹ฑ)
# ==========================================
text_result = response.text
lucky_numbers = []
# ์ œ๋ฏธ๋‚˜์ด๊ฐ€ ์ˆจ๊ฒจ๋‘” ์ฃผ์„()์—์„œ ๋ฒˆํ˜ธ๋งŒ ๋นผ์˜ค๊ธฐ
match = re.search(r'', text_result)
if match:
try:
num_str = match.group(1)
# "7, 14, 21" ๋ฌธ์ž๋ฅผ ์ˆซ์ž ๋ฆฌ์ŠคํŠธ [7, 14, 21]๋กœ ๋ณ€ํ™˜ ๋ฐ 1~45 ๋ฒ”์œ„ ์ฒดํฌ
lucky_numbers = [int(n.strip()) for n in num_str.split(',') if n.strip().isdigit()]
lucky_numbers = [n for n in lucky_numbers if 1 <= n <= 45]
lucky_numbers = list(set(lucky_numbers)) # ์ค‘๋ณต ์ œ๊ฑฐ
except Exception:
pass
# ๐Ÿ’ก [์•ˆ์ „์žฅ์น˜] ์ œ๋ฏธ๋‚˜์ด๊ฐ€ ์‹ค์ˆ˜๋กœ ๋ฒˆํ˜ธ๋ฅผ ์•ˆ ์คฌ๊ฑฐ๋‚˜ 6๊ฐœ๊ฐ€ ์•ˆ ๋  ๊ฒฝ์šฐ,
# ๋นˆ์ž๋ฆฌ๋Š” ํŒŒ์ด์ฌ์ด ์•Œ์•„์„œ ๋ฌด์ž‘์œ„ ํ–‰์šด๋ฒˆํ˜ธ๋กœ ์ฑ„์›Œ์ค๋‹ˆ๋‹ค! (์—๋Ÿฌ ๋ฐฉ์ง€)
while len(lucky_numbers) < 6:
rand_num = random.randint(1, 45)
if rand_num not in lucky_numbers:
lucky_numbers.append(rand_num)
# ๋”ฑ 6๊ฐœ๋งŒ ์ž๋ฅด๊ณ  ์˜ค๋ฆ„์ฐจ์ˆœ ์ •๋ ฌ
lucky_numbers = lucky_numbers[:6]
lucky_numbers.sort()
# ==========================================
# ํ”„๋ก ํŠธ์—”๋“œ๋กœ ํ…์ŠคํŠธ๋ฅผ ๋„˜๊ธฐ๊ธฐ ์ „์— ์ฃผ์„()์„ ์•„์˜ˆ ์ง€์›Œ๋ฒ„๋ฆผ!
text_result = re.sub(r'', '', text_result).strip()
# ==========================================
# ํ”„๋ก ํŠธ์—”๋“œ(main_saju.html)์—์„œ ์•ฝ์†ํ–ˆ๋˜ luckyNumbers ํ‚ค๊ฐ’์œผ๋กœ ์ด์ค๋‹ˆ๋‹ค!
return {"result": text_result, "luckyNumbers": lucky_numbers}
except Exception as e:
last_error = e
continue
error_msg = str(last_error)
send_slack_message(f"๐Ÿšจ [์ „์ฒด ์‹คํŒจ] ์—๋Ÿฌ: {error_msg}")
return JSONResponse(content={
'result': f"<div class='greeting'>์ฃ„์†กํ•ฉ๋‹ˆ๋‹ค. ์ ‘์†์ด ์›ํ™œํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.<br><span style='font-size:0.8rem; color:#999'>({error_msg})</span></div>"
})