好的!我幫你把今天的鐵人賽 Day14 文章生好(主題延續我們這幾天做的 LINE 金融助理:修好查股與翻譯模式)。直接貼到部落格就能用。
⸻
系列:打造一個好用又有個性的 LINE 金融助理(股價 / 匯率 / 金價 / 彩票 / 人設 / 翻譯)
🎯 今日目標
1. 修復 查股功能:避開 Yahoo API 401 Unauthorized、台股代碼相容(00937b/00937B 等)。
2. 修復 翻譯模式:開啟後,使用者輸入的每句話都直接翻譯(不中斷、不誤判)。
3. 維持 穩定輸出:OpenAI 金鑰失效時,自動改用 Groq,不影響上線。
⸻
🧩 問題現象
• Yahoo 401:直接打 https://query1.finance.yahoo.com/v7/finance/quote 常被擋,導致即時價失敗。
• 00937b 失敗:台灣 ETF/權證等常見「數字+字母」寫法,yfinance 不認。
• **翻譯模式壞掉:雖然能切換「翻譯->英文」,但之後的訊息會被一般聊天攔走,沒被翻譯。
🛠️ 解法總覽
1. 改走 HTML 解析:用 YahooStock 直接解析 Yahoo 股市頁面 DOM,拿到名稱/現價/漲跌/時間,避開 401。
2. 代碼正規化:
• 台股大盤/美股大盤:^TWII / ^GSPC。
• 台股:^\d{4,6}[A-Za-z]?$ → 自動補 .TW,英文字尾轉大寫(例:00937b → 00937B.TW)。
• 美股:^[A-Z]{1,5}$ 直接使用(排除 JPY)。
3. 翻譯直通攔截:開啟翻譯模式後,所有訊息都先走翻譯流程;只有結束才回一般聊天。
4. 模型容錯:
• 優先 OpenAI,失敗 → Groq (llama-3.1-8b-instant)。
• 模型名稱改可環境變數覆寫,避免退役爆錯。
⸻
🏗️ 關鍵程式片段
def normalize_stock_input(user_input: str) -> (str, str):
s = user_input.strip()
u = s.upper()
if u in ["台股大盤", "大盤"]:
return "^TWII", "台灣加權指數"
if u in ["美股大盤", "美盤", "美股"]:
return "^GSPC", "S&P 500 指數"
if u.endswith(".TW") or u.startswith("^"):
return u, u
# 台股:4~6位數 + 可選 1 位英文字母(大小寫都行)
if re.fullmatch(r'\d{4,6}[A-Z]?', u):
symbol = f"{u}.TW"
base = re.match(r'(\d{4,6}[A-Z]?)', u).group(1)
name = get_stock_name(base) or base
return symbol, name
if re.fullmatch(r'[A-Z]{1,5}', u) and u not in ["JPY"]:
return u, u
return u, s
if low.startswith("翻譯->"):
lang = msg.split("->", 1)[1].strip()
if lang == "結束":
translation_states.pop(chat_id, None)
return reply_with_quick_bar(reply_token, "✅ 已結束翻譯模式")
translation_states[chat_id] = lang
return reply_with_quick_bar(reply_token, f"🌐 已開啟翻譯 → {lang},請直接輸入要翻的內容。")
if chat_id in translation_states:
out = await translate_text(msg, translation_states[chat_id])
return reply_with_quick_bar(reply_token, f"🌐 ({translation_states[chat_id]})\n{out}")
3) 即時報價改走 YahooStock
newprice_stock = YahooStock(norm_code) # 解析 Yahoo HTML
price_data = stock_price(norm_code) # 走既有日K函式
news_data = str(stock_news(display_name))[:1024]
def get_analysis_reply(messages):
try:
if not openai_client: raise Exception("OpenAI not ready")
r = openai_client.chat.completions.create(model="gpt-3.5-turbo", messages=messages)
return r.choices[0].message.content
except Exception:
try:
r = sync_groq_client.chat.completions.create(
model=os.getenv("GROQ_MODEL_PRIMARY", "llama-3.1-8b-instant"),
messages=messages, max_tokens=2000, temperature=0.8
)
return r.choices[0].message.content
except Exception:
r = sync_groq_client.chat.completions.create(
model=os.getenv("GROQ_MODEL_FALLBACK", "llama-3.1-8b-instant"),
messages=messages, max_tokens=1500, temperature=1.0
)
return r.choices[0].message.content
⸻
✅ 驗收清單
• 2330、2002、00937b/00937B 可查,顯示正確名稱/現價/漲跌/時間。
• 台股大盤 / 美股大盤 正確對應 ^TWII / ^GSPC。
• NVDA、QQQ 等美股代碼可查。
• 「翻譯->英文」後,任何文字都直接翻譯;「翻譯->結束」恢復聊天。
• OpenAI 401 時,自動切到 Groq;不中斷服務。
⸻
🧪 測試案例
1. 台股 ETF:輸入 00937b → 內部標準化為 00937B.TW → 正常報價。
2. 大盤:輸入 台股大盤 → 指定 ^TWII → 輸出指數報告(不抓營收/配息)。
3. 翻譯:
• 翻譯->英文 → 你好 → Hello
• 翻譯->日文 → 今天下雨嗎? → 今日は雨が降りますか?
• 翻譯->結束 → 回到一般聊天。
⸻
🗂️ 今天的提交
• app_fastapi.py:主流程修正(翻譯直通 / 代碼正規化 / 容錯 / 介面維持)。
• YahooStock.py:沿用 HTML 解析方案(避免 Yahoo 401)。
⸻
🔭 可能可以加入的功能
• 新增 錯誤回饋卡片(顯示錯誤原因 & 自助檢查步驟)。
• 引入 指令別名(/tw 查台股、/us 查美股…),讓群組裡更好用。
• 加上 快取層:短時間重複查詢直接回覆快取,省時省流量。
** 經測試etf的資訊會少一點