mylinebotnew / gemini.py
wsy07's picture
Update gemini.py
180e4eb verified
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)]
)
)