Spaces:
Sleeping
Sleeping
File size: 5,140 Bytes
887f465 9949813 887f465 096d13f 8f62348 7431696 9949813 f3ead48 b8e6e12 f3ead48 9949813 f3ead48 b8e6e12 8f62348 9949813 887f465 f3ead48 8f62348 f1dd422 f3ead48 79d4f9b f3ead48 2b0943a 8da82cb 8f62348 8da82cb f1dd422 f3ead48 887f465 bbbbe16 887f465 84e80ee 887f465 f1dd422 887f465 9949813 f1dd422 9949813 887f465 f3ead48 8f62348 887f465 9949813 bbbbe16 9949813 f1dd422 26174f2 8f62348 180e4eb 8f62348 f1dd422 9949813 f3ead48 7431696 84e80ee 9949813 7431696 f1dd422 7431696 096d13f 7431696 8f62348 7431696 8f62348 9949813 7431696 8f62348 bbbbe16 8f62348 ce94612 8f62348 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 |
import logging
import os
import tempfile
import uuid
from io import BytesIO
import markdown
from bs4 import BeautifulSoup
from flask import Flask, abort, request, send_from_directory
from google import genai
from google.genai import types
from google.genai.types import Tool, GenerateContentConfig, GoogleSearch
from linebot.v3 import WebhookHandler
from linebot.v3.exceptions import InvalidSignatureError
from linebot.v3.messaging import (
ApiClient,
Configuration,
ImageMessage,
MessagingApi,
MessagingApiBlob,
ReplyMessageRequest,
TextMessage,
)
from linebot.v3.webhooks import (
ImageMessageContent,
MessageEvent,
TextMessageContent,
)
from PIL import Image
from linebot.v3.webhooks import VideoMessageContent
# === 初始化 Google Gemini ===
GOOGLE_API_KEY = os.environ.get("GOOGLE_API_KEY")
client = genai.Client(api_key=GOOGLE_API_KEY)
google_search_tool = Tool(
google_search=GoogleSearch()
)
chat = client.chats.create(
model="gemini-2.0-flash",
config=GenerateContentConfig(
system_instruction=(
"你是一位專業健身教練與營養顧問,擁有多年重量訓練與健身飲食指導經驗。"
"請使用繁體中文,針對使用者的健身問題提供專業建議,包含動作教學、訓練計畫、飲食建議與常見錯誤修正等。回答請控制在 200 字內。"
),
tools=[google_search_tool],
response_modalities=["TEXT"],
)
)
# === 初始設定 ===
static_tmp_path = tempfile.gettempdir()
os.makedirs(static_tmp_path, exist_ok=True)
base_url = os.getenv("SPACE_HOST")
app = Flask(__name__)
logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s")
app.logger.setLevel(logging.INFO)
channel_secret = os.environ.get("YOUR_CHANNEL_SECRET")
channel_access_token = os.environ.get("YOUR_CHANNEL_ACCESS_TOKEN")
configuration = Configuration(access_token=channel_access_token)
handler = WebhookHandler(channel_secret)
def query(payload):
response = chat.send_message(message=payload)
return response.text[:200] # 回覆字數上限 200 字
@app.route("/images/<filename>")
def serve_image(filename):
return send_from_directory(static_tmp_path, filename)
@app.route("/")
def home():
return {"message": "Line Webhook Server"}
@app.route("/", methods=["POST"])
def callback():
signature = request.headers.get("X-Line-Signature")
body = request.get_data(as_text=True)
app.logger.info(f"Request body: {body}")
try:
handler.handle(body, signature)
except InvalidSignatureError:
app.logger.warning("Invalid signature. Please check channel credentials.")
abort(400)
return "OK"
@handler.add(MessageEvent, message=TextMessageContent)
def handle_text_message(event):
user_msg = event.message.text.strip()
if user_msg == "飲食紀錄":
reply = "點選下方連結開始記錄你的飲食 🍱:\nhttps://docs.google.com/forms/d/e/1FAIpQLScrNcLDvfODtj7V0IEmo_MMBUQG1LA3HfvbNsraM4-mQmJlOA/viewform?usp=dialog"
elif user_msg == "訓練紀錄":
reply = "點選下方連結記錄你的訓練 💪:\nhttps://docs.google.com/forms/d/e/1FAIpQLSc8crIyxQX-YJeaNzM5x1JUWbQA-qoQPiZB9cbqKuLOq3uQpA/viewform"
elif user_msg == "運動補給品":
reply = "推薦常見健身補給品:乳清蛋白、BCAA、肌酸與電解質飲。點此選購 👉 https://www.myprotein.tw/"
elif user_msg.startswith("AI "):
prompt = user_msg[3:].strip()
try:
response = client.models.generate_content(
model="gemini-2.0-flash-exp-image-generation",
contents=prompt,
config=types.GenerateContentConfig(response_modalities=["TEXT", "IMAGE"]),
)
for part in response.candidates[0].content.parts:
if part.inline_data is not None:
image = Image.open(BytesIO(part.inline_data.data))
filename = f"{uuid.uuid4().hex}.png"
image_path = os.path.join(static_tmp_path, filename)
image.save(image_path)
image_url = f"https://{base_url}/images/{filename}"
with ApiClient(configuration) as api_client:
line_bot_api = MessagingApi(api_client)
line_bot_api.reply_message(
ReplyMessageRequest(
reply_token=event.reply_token,
messages=[ImageMessage(original_content_url=image_url, preview_image_url=image_url)],
)
)
return
except Exception as e:
reply = "抱歉,生成圖片時發生錯誤。"
else:
reply = query(user_msg)
with ApiClient(configuration) as api_client:
line_bot_api = MessagingApi(api_client)
line_bot_api.reply_message(
ReplyMessageRequest(
reply_token=event.reply_token,
messages=[TextMessage(text=reply)]
)
)
|