sculinebot2025 / test.py
3v324v23's picture
็”Ÿๅœ–ๆ›ๆˆgoogle
85aca89
raw
history blame
7.33 kB
# ===ๆฑๅณๅคงๅญธ่ณ‡ๆ–™็ณป 2025 ๅนด LINEBOT ===
import base64
import logging
import os
import tempfile
from io import BytesIO
from google import genai
from google.genai import types
from PIL import Image
import markdown
from bs4 import BeautifulSoup
from flask import Flask, abort, request, send_from_directory
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 openai import OpenAI
# === ๅˆๅง‹ๅŒ– Google Gemini ===
GOOGLE_API_KEY = os.environ.get("GOOGLE_API_KEY")
google_client = genai.Client(api_key=GOOGLE_API_KEY)
# === ๅˆๅง‹ๅŒ–OpenAIๆจกๅž‹ ===
OPENAI_API_KEY = os.getenv("OPENAI_API_KEY")
client = OpenAI(api_key=OPENAI_API_KEY)
text_system_prompt = "ไฝ ๆ˜ฏไธ€ๅ€‹ไธญๆ–‡็š„AIๅŠฉๆ‰‹๏ผŒ่ซ‹็”จ็น้ซ”ไธญๆ–‡ๅ›ž็ญ”"
# === ๅˆๅง‹่จญๅฎš ===
static_tmp_path = tempfile.gettempdir()
os.makedirs(static_tmp_path, exist_ok=True)
base_url = os.getenv("SPACE_HOST") # e.g., "your-space-name.hf.space"
# === Flask ๆ‡‰็”จๅˆๅง‹ๅŒ– ===
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)
# === AI Query ๅŒ…่ฃ ===
def query(payload):
response = google_client.models.generate_content(
model="gemini-2.0-flash",
contents=f"{text_system_prompt}๏ผš{payload}",
)
return response.text
# === ้œๆ…‹ๅœ–ๆช”่ทฏ็”ฑ ===
@app.route("/images/<filename>")
def serve_image(filename):
return send_from_directory(static_tmp_path, filename)
# === LINE Webhook ๆŽฅๆ”ถ็ซฏ้ปž ===
@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_input = event.message.text.strip()
if user_input.startswith("AI "):
prompt = user_input[3:].strip()
try:
# ไฝฟ็”จ Gemini ็”Ÿๆˆๅœ–็‰‡
response = google_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 = "output.png"
image_path = os.path.join(static_tmp_path, filename)
image.save(image_path)
# ๅปบ็ซ‹ๅœ–็‰‡็š„ๅ…ฌ้–‹ URL
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,
)
],
)
)
except Exception as e:
app.logger.error(f"Gemini API error: {e}")
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="ๆŠฑๆญ‰๏ผŒ็”Ÿๆˆๅœ–็‰‡ๆ™‚็™ผ็”Ÿ้Œฏ่ชคใ€‚")],
)
)
else:
with ApiClient(configuration) as api_client:
line_bot_api = MessagingApi(api_client)
response = query(event.message.text)
html_msg = markdown.markdown(response)
soup = BeautifulSoup(html_msg, "html.parser")
line_bot_api.reply_message_with_http_info(
ReplyMessageRequest(
reply_token=event.reply_token,
messages=[TextMessage(text=soup.get_text())],
)
)
# === ่™•็†ๅœ–็‰‡่จŠๆฏ ===
@handler.add(MessageEvent, message=ImageMessageContent)
def handle_image_message(event):
# === ไปฅไธ‹ๆ˜ฏ่™•็†ๅœ–็‰‡ๅ›žๅ‚ณ้ƒจๅˆ† === #
with ApiClient(configuration) as api_client:
blob_api = MessagingApiBlob(api_client)
content = blob_api.get_message_content(message_id=event.message.id)
image_bytes = content
# Step 2๏ผš่ฝ‰ๆˆ base64 ๅญ—ไธฒ
base64_string = base64.b64encode(image_bytes).decode("utf-8")
# Step 3๏ผš็ต„ๆˆ OpenAI ็š„ data URI ๆ ผๅผ
data_uri = f"data:image/png;base64,{base64_string}"
app.logger.info(f"Data URI: {data_uri}")
# Step 4๏ผšๅฐ‡ๅœ–็‰‡ๅญ˜ๅˆฐๆœฌๅœฐ็ซฏ
with tempfile.NamedTemporaryFile(
dir=static_tmp_path, suffix=".jpg", delete=False
) as tf:
tf.write(content)
filename = os.path.basename(tf.name)
image_url = f"https://{base_url}/images/{filename}"
app.logger.info(f"Image URL: {image_url}")
# === ไปฅไธ‹ๆ˜ฏ่™•็†่งฃ้‡‹ๅœ–็‰‡้ƒจๅˆ† === #
response = client.responses.create(
model="gpt-4.1-nano",
input=[
{
"role": "user",
"content": [
{
"type": "input_text",
"text": "describe the image in traditional chinese",
},
{
"type": "input_image",
"image_url": data_uri,
},
],
}
],
)
app.logger.info(response.output_text)
# === ไปฅไธ‹ๆ˜ฏๅ›žๅ‚ณๅœ–็‰‡้ƒจๅˆ† === #
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
),
TextMessage(text=response.output_text),
],
)
)