Spaces:
Sleeping
Sleeping
Update main.py
Browse files
main.py
CHANGED
@@ -8,15 +8,19 @@ from linebot.exceptions import InvalidSignatureError
|
|
8 |
from linebot.models import MessageEvent, TextMessage, TextSendMessage, ImageSendMessage, AudioMessage
|
9 |
from dotenv import load_dotenv
|
10 |
|
11 |
-
#
|
12 |
load_dotenv()
|
13 |
|
14 |
-
#
|
15 |
-
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
|
16 |
|
17 |
|
18 |
-
# --- 1.
|
19 |
def configure_gemini():
|
|
|
|
|
|
|
|
|
20 |
try:
|
21 |
genai.configure(api_key=os.getenv("GOOGLE_API_KEY"))
|
22 |
generation_config = genai.types.GenerationConfig(
|
@@ -26,38 +30,42 @@ def configure_gemini():
|
|
26 |
"gemini-2.0-flash-exp",
|
27 |
system_instruction="你是咖啡廳員工,請使用親切的招呼語做為開頭,然後以爽朗愉悅的口氣回答問題。",
|
28 |
)
|
29 |
-
logging.info("Gemini
|
30 |
return model, generation_config
|
31 |
except Exception as e:
|
32 |
-
logging.error(f"
|
33 |
raise
|
34 |
|
35 |
|
36 |
def configure_line_bot():
|
|
|
|
|
|
|
|
|
37 |
try:
|
38 |
line_bot_api = LineBotApi(os.getenv("CHANNEL_ACCESS_TOKEN"))
|
39 |
line_handler = WebhookHandler(os.getenv("CHANNEL_SECRET"))
|
40 |
-
logging.info("Line Bot API
|
41 |
return line_bot_api, line_handler
|
42 |
except Exception as e:
|
43 |
-
logging.error(f"
|
44 |
raise
|
45 |
|
46 |
-
#
|
47 |
try:
|
48 |
model, generation_config = configure_gemini()
|
49 |
line_bot_api, line_handler = configure_line_bot()
|
50 |
except Exception as e:
|
51 |
-
logging.critical(f"
|
52 |
-
exit(1) #
|
53 |
|
54 |
-
#
|
55 |
working_status = os.getenv("DEFALUT_TALKING", default="true").lower() == "true"
|
56 |
|
57 |
-
# --- 2. FastAPI
|
58 |
app = FastAPI()
|
59 |
|
60 |
-
#
|
61 |
app.add_middleware(
|
62 |
CORSMiddleware,
|
63 |
allow_origins=["*"],
|
@@ -66,9 +74,12 @@ app.add_middleware(
|
|
66 |
allow_headers=["*"],
|
67 |
)
|
68 |
|
69 |
-
# --- 3.
|
70 |
@app.get("/")
|
71 |
def root():
|
|
|
|
|
|
|
72 |
return {"title": "Line Bot"}
|
73 |
|
74 |
|
@@ -76,22 +87,30 @@ def root():
|
|
76 |
async def webhook(
|
77 |
request: Request, background_tasks: BackgroundTasks, x_line_signature=Header(None)
|
78 |
):
|
|
|
|
|
|
|
|
|
79 |
body = await request.body()
|
80 |
try:
|
81 |
background_tasks.add_task(
|
82 |
line_handler.handle, body.decode("utf-8"), x_line_signature
|
83 |
)
|
84 |
except InvalidSignatureError:
|
85 |
-
logging.warning("
|
86 |
-
raise HTTPException(status_code=400, detail="
|
87 |
return "ok"
|
88 |
|
89 |
-
# --- 4.
|
90 |
chat_sessions = {}
|
91 |
|
92 |
def get_or_create_chat_session(user_id: str):
|
|
|
|
|
|
|
|
|
93 |
if user_id not in chat_sessions:
|
94 |
-
logging.info(f"
|
95 |
chat_sessions[user_id] = model.start_chat(
|
96 |
history=[
|
97 |
{
|
@@ -107,6 +126,9 @@ def get_or_create_chat_session(user_id: str):
|
|
107 |
return chat_sessions[user_id]
|
108 |
|
109 |
def handle_text_message(event):
|
|
|
|
|
|
|
110 |
global working_status
|
111 |
|
112 |
user_id = event.source.user_id
|
@@ -114,40 +136,44 @@ def handle_text_message(event):
|
|
114 |
line_bot_api.reply_message(event.reply_token, TextSendMessage(text="Bye!"))
|
115 |
if user_id in chat_sessions:
|
116 |
del chat_sessions[user_id]
|
117 |
-
logging.info(f"
|
118 |
return
|
119 |
|
120 |
if not working_status:
|
121 |
-
logging.info("
|
122 |
return
|
123 |
|
124 |
try:
|
125 |
prompt = event.message.text
|
126 |
-
logging.info(f"
|
127 |
chat_session = get_or_create_chat_session(user_id)
|
128 |
response = chat_session.send_message(prompt)
|
129 |
if response.text:
|
130 |
out = response.text
|
131 |
-
logging.info(f"Gemini
|
132 |
else:
|
133 |
out = "Gemini 沒答案!請換個說法!"
|
134 |
-
logging.warning(f"Gemini
|
135 |
except Exception as e:
|
136 |
out = "Gemini 執行出錯!請換個說法!"
|
137 |
-
logging.error(f"Gemini
|
138 |
|
139 |
line_bot_api.reply_message(event.reply_token, TextSendMessage(text=out))
|
140 |
|
141 |
@line_handler.add(MessageEvent, message=TextMessage)
|
142 |
def handle_message(event):
|
|
|
|
|
|
|
|
|
143 |
if event.type != "message" or event.message.type != "text":
|
144 |
-
logging.warning("
|
145 |
-
line_bot_api.reply_message(event.reply_token, TextSendMessage(text="
|
146 |
return
|
147 |
|
148 |
handle_text_message(event)
|
149 |
|
150 |
-
# --- 5.
|
151 |
if __name__ == "__main__":
|
152 |
import uvicorn
|
153 |
|
|
|
8 |
from linebot.models import MessageEvent, TextMessage, TextSendMessage, ImageSendMessage, AudioMessage
|
9 |
from dotenv import load_dotenv
|
10 |
|
11 |
+
# 從 .env 檔案載入環境變數
|
12 |
load_dotenv()
|
13 |
|
14 |
+
# 設定日誌記錄
|
15 |
+
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s', encoding='utf-8')
|
16 |
|
17 |
|
18 |
+
# --- 1. 配置設定 ---
|
19 |
def configure_gemini():
|
20 |
+
"""
|
21 |
+
配置 Google Gemini 模型
|
22 |
+
設定 API 金鑰、生成參數和模型
|
23 |
+
"""
|
24 |
try:
|
25 |
genai.configure(api_key=os.getenv("GOOGLE_API_KEY"))
|
26 |
generation_config = genai.types.GenerationConfig(
|
|
|
30 |
"gemini-2.0-flash-exp",
|
31 |
system_instruction="你是咖啡廳員工,請使用親切的招呼語做為開頭,然後以爽朗愉悅的口氣回答問題。",
|
32 |
)
|
33 |
+
logging.info("Gemini 模型配置完成.")
|
34 |
return model, generation_config
|
35 |
except Exception as e:
|
36 |
+
logging.error(f"Gemini 模型配置錯誤: {e}")
|
37 |
raise
|
38 |
|
39 |
|
40 |
def configure_line_bot():
|
41 |
+
"""
|
42 |
+
配置 Line Bot API
|
43 |
+
設定 Channel Access Token 和 Channel Secret
|
44 |
+
"""
|
45 |
try:
|
46 |
line_bot_api = LineBotApi(os.getenv("CHANNEL_ACCESS_TOKEN"))
|
47 |
line_handler = WebhookHandler(os.getenv("CHANNEL_SECRET"))
|
48 |
+
logging.info("Line Bot API 配置完成.")
|
49 |
return line_bot_api, line_handler
|
50 |
except Exception as e:
|
51 |
+
logging.error(f"Line Bot API 配置錯誤: {e}")
|
52 |
raise
|
53 |
|
54 |
+
# 初始化配置
|
55 |
try:
|
56 |
model, generation_config = configure_gemini()
|
57 |
line_bot_api, line_handler = configure_line_bot()
|
58 |
except Exception as e:
|
59 |
+
logging.critical(f"初始化配置失敗: {e}")
|
60 |
+
exit(1) # 如果配置失敗,則終止程式
|
61 |
|
62 |
+
# 從環境變數取得是否啟用對話模式,預設為 true
|
63 |
working_status = os.getenv("DEFALUT_TALKING", default="true").lower() == "true"
|
64 |
|
65 |
+
# --- 2. FastAPI 應用程式 ---
|
66 |
app = FastAPI()
|
67 |
|
68 |
+
# 設定 CORS,允許跨域請求
|
69 |
app.add_middleware(
|
70 |
CORSMiddleware,
|
71 |
allow_origins=["*"],
|
|
|
74 |
allow_headers=["*"],
|
75 |
)
|
76 |
|
77 |
+
# --- 3. 路由設定 ---
|
78 |
@app.get("/")
|
79 |
def root():
|
80 |
+
"""
|
81 |
+
處理根路徑請求,返回 Line Bot 的標題
|
82 |
+
"""
|
83 |
return {"title": "Line Bot"}
|
84 |
|
85 |
|
|
|
87 |
async def webhook(
|
88 |
request: Request, background_tasks: BackgroundTasks, x_line_signature=Header(None)
|
89 |
):
|
90 |
+
"""
|
91 |
+
處理 Line Webhook 請求
|
92 |
+
接收 Line 的訊息並驗證簽章,然後將訊息處理加入背景任務
|
93 |
+
"""
|
94 |
body = await request.body()
|
95 |
try:
|
96 |
background_tasks.add_task(
|
97 |
line_handler.handle, body.decode("utf-8"), x_line_signature
|
98 |
)
|
99 |
except InvalidSignatureError:
|
100 |
+
logging.warning("收到無效的簽章.")
|
101 |
+
raise HTTPException(status_code=400, detail="無效的簽章")
|
102 |
return "ok"
|
103 |
|
104 |
+
# --- 4. 對話 Session 管理 ---
|
105 |
chat_sessions = {}
|
106 |
|
107 |
def get_or_create_chat_session(user_id: str):
|
108 |
+
"""
|
109 |
+
取得或建立特定使用者的對話 Session
|
110 |
+
如果不存在,則建立一個新的 Session
|
111 |
+
"""
|
112 |
if user_id not in chat_sessions:
|
113 |
+
logging.info(f"為使用者 {user_id} 建立新的對話 Session")
|
114 |
chat_sessions[user_id] = model.start_chat(
|
115 |
history=[
|
116 |
{
|
|
|
126 |
return chat_sessions[user_id]
|
127 |
|
128 |
def handle_text_message(event):
|
129 |
+
"""
|
130 |
+
處理文字訊息,取得使用者輸入,並回覆 Gemini 模型的訊息
|
131 |
+
"""
|
132 |
global working_status
|
133 |
|
134 |
user_id = event.source.user_id
|
|
|
136 |
line_bot_api.reply_message(event.reply_token, TextSendMessage(text="Bye!"))
|
137 |
if user_id in chat_sessions:
|
138 |
del chat_sessions[user_id]
|
139 |
+
logging.info(f"使用者 {user_id} 說再見,已刪除對話 Session")
|
140 |
return
|
141 |
|
142 |
if not working_status:
|
143 |
+
logging.info("對話模式關閉,忽略訊息.")
|
144 |
return
|
145 |
|
146 |
try:
|
147 |
prompt = event.message.text
|
148 |
+
logging.info(f"收到使用者 {user_id} 的訊息: {prompt}")
|
149 |
chat_session = get_or_create_chat_session(user_id)
|
150 |
response = chat_session.send_message(prompt)
|
151 |
if response.text:
|
152 |
out = response.text
|
153 |
+
logging.info(f"Gemini 回覆使用者 {user_id}: {out}")
|
154 |
else:
|
155 |
out = "Gemini 沒答案!請換個說法!"
|
156 |
+
logging.warning(f"Gemini 沒有回覆文字內容給使用者 {user_id}")
|
157 |
except Exception as e:
|
158 |
out = "Gemini 執行出錯!請換個說法!"
|
159 |
+
logging.error(f"Gemini 發生錯誤 (使用者: {user_id}): {e}")
|
160 |
|
161 |
line_bot_api.reply_message(event.reply_token, TextSendMessage(text=out))
|
162 |
|
163 |
@line_handler.add(MessageEvent, message=TextMessage)
|
164 |
def handle_message(event):
|
165 |
+
"""
|
166 |
+
Line Bot 訊息處理的主要函式
|
167 |
+
驗證訊息類型,然後將文字訊息轉發給處理函數
|
168 |
+
"""
|
169 |
if event.type != "message" or event.message.type != "text":
|
170 |
+
logging.warning("事件類型錯誤:不是文字訊息.")
|
171 |
+
line_bot_api.reply_message(event.reply_token, TextSendMessage(text="事件類型錯誤:不是文字訊息。"))
|
172 |
return
|
173 |
|
174 |
handle_text_message(event)
|
175 |
|
176 |
+
# --- 5. 啟動應用程式 ---
|
177 |
if __name__ == "__main__":
|
178 |
import uvicorn
|
179 |
|