Skip to content

Commit 640f7ef

Browse files
authored
Merge pull request #3 from gdsc-ncku/refactor-service
feat: refactor message handling into dedicated handler modules
2 parents 6091a12 + 7c6be67 commit 640f7ef

7 files changed

Lines changed: 198 additions & 125 deletions

File tree

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# 匯出所有處理函式
2+
from .text_handler import handle_text_message
3+
from .image_handler import handle_image_message
4+
from .audio_handler import handle_audio_message
5+
from .common import send_message, create_quick_reply
6+
7+
__all__ = [
8+
"handle_text_message",
9+
"handle_image_message",
10+
"handle_audio_message",
11+
"send_message",
12+
"create_quick_reply",
13+
]
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
"""處理音訊訊息的模組"""
2+
3+
import logging
4+
from linebot.models import TextSendMessage
5+
from .common import create_quick_reply
6+
7+
logger = logging.getLogger(__name__)
8+
9+
10+
def handle_audio_message(event):
11+
"""處理音訊訊息"""
12+
return [TextSendMessage(text="收到音訊消息", quick_reply=create_quick_reply())]
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
"""共用函式模組"""
2+
3+
import logging
4+
from linebot.models import (
5+
QuickReply,
6+
QuickReplyButton,
7+
MessageAction,
8+
SendMessage,
9+
)
10+
from ...config.line_config import line_bot_api
11+
12+
logger = logging.getLogger(__name__)
13+
14+
# 常用指令
15+
COMMANDS = {
16+
"/help": (
17+
"📚 可用的指令列表:\n"
18+
"1. /help - 顯示此幫助訊息\n"
19+
"2. /setup - 設定相關功能\n\n"
20+
"💡 您也可以直接輸入問題,我會盡力協助您!"
21+
),
22+
"/setup": "⚙️ 設定功能開發中,敬請期待!",
23+
}
24+
25+
26+
def create_quick_reply() -> QuickReply:
27+
"""建立快速回覆按鈕"""
28+
return QuickReply(
29+
items=[
30+
QuickReplyButton(action=MessageAction(label="說明", text="/help")),
31+
QuickReplyButton(action=MessageAction(label="設定", text="/setup")),
32+
]
33+
)
34+
35+
36+
def send_message(reply_token: str, messages: list[SendMessage]) -> None:
37+
"""發送訊息到 LINE"""
38+
readable_messages = str(messages).encode("utf-8").decode("unicode_escape")
39+
logger.info(f"準備發送訊息 (使用可讀字串): {readable_messages}")
40+
41+
# 確保 messages 是一個扁平化的訊息列表 (因為可能有巢狀的訊息列表)
42+
flat_messages = []
43+
for msg in messages:
44+
if isinstance(msg, list):
45+
flat_messages.extend(msg) # 如果是列表,則展開
46+
else:
47+
flat_messages.append(msg) # 如果是單一訊息,則直接加入
48+
49+
line_bot_api.reply_message(reply_token, flat_messages)
50+
logger.info(f"已發送訊息: {flat_messages}")
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
"""處理圖片訊息的模組"""
2+
3+
import os
4+
import logging
5+
from linebot.models import TextSendMessage
6+
from .common import create_quick_reply
7+
from ...api.vision import process_image
8+
from ...config.line_config import line_bot_api
9+
10+
logger = logging.getLogger(__name__)
11+
12+
13+
def handle_image_message(event):
14+
"""處理圖片訊息"""
15+
try:
16+
# 取得圖片內容
17+
message_id = event.message.id
18+
message_content = line_bot_api.get_message_content(message_id)
19+
20+
# 建立臨時檔案保存圖片
21+
temp_file_path = f"{message_id}.jpg"
22+
with open(temp_file_path, "wb") as temp_image_file:
23+
for chunk in message_content.iter_content():
24+
temp_image_file.write(chunk)
25+
26+
# 使用 vision 函式處理圖片
27+
response = process_image(
28+
temp_file_path, "圖片內容有什麼?"
29+
) # TODO: 設定圖片處理的 prompt
30+
# 刪除臨時檔案
31+
os.remove(temp_file_path)
32+
return [TextSendMessage(text=response, quick_reply=create_quick_reply())]
33+
except Exception as e:
34+
logger.error(f"處理圖片訊息時發生錯誤: {str(e)}", exc_info=True)
35+
return [
36+
TextSendMessage(
37+
text="抱歉,處理圖片時發生錯誤,請稍後再試。",
38+
quick_reply=create_quick_reply(),
39+
)
40+
]
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
"""處理文字訊息的模組"""
2+
3+
import json
4+
import logging
5+
from linebot.models import TextSendMessage, FlexSendMessage
6+
from .common import create_quick_reply, COMMANDS
7+
from ...api.dify import inference
8+
9+
logger = logging.getLogger(__name__)
10+
11+
12+
def handle_text_message(event):
13+
"""處理文字訊息"""
14+
try:
15+
user_input = event.message.text
16+
user_id = event.source.user_id
17+
18+
# 產生回應訊息
19+
quick_reply = None
20+
if user_input in COMMANDS:
21+
response_text = COMMANDS[user_input]
22+
quick_reply = create_quick_reply()
23+
return [TextSendMessage(text=response_text, quick_reply=quick_reply)]
24+
25+
# 處理一般查詢
26+
response_text = inference(user_input, user_id)
27+
quick_reply = create_quick_reply()
28+
29+
# 處理可能包含 Flex Message 的回應
30+
if "===FLEX_MESSAGE===" in response_text:
31+
parts = response_text.split("===FLEX_MESSAGE===")
32+
text_content = parts[0].strip()
33+
text_message = TextSendMessage(text=text_content, quick_reply=quick_reply)
34+
35+
# 檢查是否有 Flex Message 部分
36+
if len(parts) > 1:
37+
flex_content = parts[1].strip().replace("```", "").replace("json", "")
38+
if flex_content and flex_content != "False":
39+
try:
40+
flex_json = json.loads(flex_content)
41+
return [
42+
text_message,
43+
FlexSendMessage(alt_text="詳細資訊", contents=flex_json),
44+
]
45+
except Exception as e:
46+
logger.error(f"Flex訊息解析錯誤: {str(e)}", exc_info=True)
47+
48+
return [text_message]
49+
50+
# 純文字回應
51+
return [TextSendMessage(text=response_text, quick_reply=quick_reply)]
52+
53+
except Exception as e:
54+
logger.error(f"處理訊息時發生錯誤: {str(e)}", exc_info=True)
55+
return [
56+
TextSendMessage(
57+
text="抱歉,系統發生錯誤,請稍後再試。",
58+
quick_reply=create_quick_reply(),
59+
)
60+
]
Lines changed: 17 additions & 119 deletions
Original file line numberDiff line numberDiff line change
@@ -1,134 +1,32 @@
1-
import logging
2-
import json
1+
"""訊息服務模組 - 處理 LINE Bot 的各種訊息類型"""
32

4-
from linebot.models import (
5-
TextSendMessage,
6-
QuickReply,
7-
QuickReplyButton,
8-
MessageAction,
9-
SendMessage,
10-
FlexSendMessage,
3+
import logging
4+
from .handlers import (
5+
handle_text_message,
6+
handle_image_message,
7+
handle_audio_message,
8+
send_message,
119
)
12-
from ..api.vision import process_image
13-
from ..config.line_config import line_bot_api
14-
from ..api.dify import inference
1510

1611
logger = logging.getLogger(__name__)
1712

18-
COMMANDS = {
19-
"/help": (
20-
"📚 可用的指令列表:\n"
21-
"1. /help - 顯示此幫助訊息\n"
22-
"2. /setup - 設定相關功能\n\n"
23-
"💡 您也可以直接輸入問題,我會盡力協助您!"
24-
),
25-
"/setup": "⚙️ 設定功能開發中,敬請期待!",
26-
}
27-
2813

14+
# 為了保持與舊版API相容,我們可以保留這個類別作為包裝器
2915
class MessageService:
30-
"""處理 LINE Bot 的訊息服務"""
16+
"""處理 LINE Bot 的訊息服務 (相容性封裝)"""
3117

32-
def handle_text_message(self, event) -> TextSendMessage:
18+
def handle_text_message(self, event):
3319
"""處理文字訊息"""
34-
try:
35-
user_input = event.message.text
36-
user_id = event.source.user_id
37-
38-
# 產生回應訊息
39-
quick_reply = None
40-
if user_input in COMMANDS:
41-
response_text = COMMANDS[user_input]
42-
quick_reply = self._create_quick_reply()
43-
return [TextSendMessage(text=response_text, quick_reply=quick_reply)]
44-
45-
# 處理一般查詢
46-
response_text = inference(user_input, user_id)
47-
quick_reply = self._create_quick_reply()
48-
49-
# 處理可能包含 Flex Message 的回應
50-
if "===FLEX_MESSAGE===" in response_text:
51-
parts = response_text.split("===FLEX_MESSAGE===")
52-
text_content = parts[0].strip()
53-
text_message = TextSendMessage(
54-
text=text_content, quick_reply=quick_reply
55-
)
56-
57-
# 檢查是否有 Flex Message 部分
58-
if len(parts) > 1:
59-
flex_content = (
60-
parts[1].strip().replace("```", "").replace("json", "")
61-
)
62-
if flex_content and flex_content != "False":
63-
try:
64-
flex_json = json.loads(flex_content)
65-
return [
66-
text_message,
67-
FlexSendMessage(
68-
alt_text="詳細資訊", contents=flex_json
69-
),
70-
]
71-
except Exception as e:
72-
logger.error(f"Flex訊息解析錯誤: {str(e)}", exc_info=True)
73-
74-
return [text_message]
20+
return handle_text_message(event)
7521

76-
# 純文字回應
77-
return [TextSendMessage(text=response_text, quick_reply=quick_reply)]
78-
79-
except Exception as e:
80-
logger.error(f"處理訊息時發生錯誤: {str(e)}", exc_info=True)
81-
return TextSendMessage(
82-
text="抱歉,系統發生錯誤,請稍後再試。",
83-
quick_reply=self._create_quick_reply(),
84-
)
85-
86-
def handle_image_message(self, event) -> TextSendMessage:
22+
def handle_image_message(self, event):
8723
"""處理圖片訊息"""
88-
try:
89-
# 取得圖片內容
90-
message_id = event.message.id
91-
message_content = line_bot_api.get_message_content(message_id)
92-
93-
# 建立臨時檔案保存圖片
94-
temp_file_path = f"{message_id}.jpg"
95-
with open(temp_file_path, "wb") as temp_image_file:
96-
for chunk in message_content.iter_content():
97-
temp_image_file.write(chunk)
98-
99-
# 使用 vision 函式處理圖片
100-
response = process_image(
101-
temp_file_path, "圖片內容有什麼?"
102-
) # TODO: 設定圖片處理的 prompt
24+
return handle_image_message(event)
10325

104-
# 刪除臨時檔案
105-
os.remove(temp_file_path)
106-
107-
return TextSendMessage(
108-
text=response, quick_reply=self._create_quick_reply()
109-
)
110-
except Exception as e:
111-
logger.error(f"處理圖片訊息時發生錯誤: {str(e)}", exc_info=True)
112-
return TextSendMessage(
113-
text="抱歉,處理圖片時發生錯誤,請稍後再試。",
114-
quick_reply=self._create_quick_reply(),
115-
)
116-
117-
def handle_audio_message(self, event) -> TextSendMessage:
26+
def handle_audio_message(self, event):
11827
"""處理音訊訊息"""
119-
return TextSendMessage(
120-
text="收到音訊消息", quick_reply=self._create_quick_reply()
121-
)
28+
return handle_audio_message(event)
12229

123-
def send_message(self, reply_token: str, messages: list[SendMessage]) -> None:
30+
def send_message(self, reply_token, messages):
12431
"""發送訊息到 LINE"""
125-
line_bot_api.reply_message(reply_token, messages)
126-
127-
def _create_quick_reply(self) -> QuickReply:
128-
"""建立快速回覆按鈕"""
129-
return QuickReply(
130-
items=[
131-
QuickReplyButton(action=MessageAction(label="說明", text="/help")),
132-
QuickReplyButton(action=MessageAction(label="設定", text="/setup")),
133-
]
134-
)
32+
send_message(reply_token, messages)

linebot/app/webhooks/handlers.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,19 +9,19 @@
99
# text message
1010
@handler.add(MessageEvent, message=TextMessage)
1111
def handle_message(event):
12-
reply_message = message_service.handle_text_message(event)
13-
message_service.send_message(event.reply_token, [reply_message])
12+
reply_messages = message_service.handle_text_message(event)
13+
message_service.send_message(event.reply_token, reply_messages)
1414

1515

1616
# image message
1717
@handler.add(MessageEvent, message=ImageMessage)
1818
def handle_image_message(event):
19-
reply_message = message_service.handle_image_message(event)
20-
message_service.send_message(event.reply_token, [reply_message])
19+
reply_messages = message_service.handle_image_message(event)
20+
message_service.send_message(event.reply_token, reply_messages)
2121

2222

2323
# audio message
2424
@handler.add(MessageEvent, message=AudioMessage)
2525
def handle_audio_message(event):
26-
reply_message = message_service.handle_audio_message(event)
27-
message_service.send_message(event.reply_token, [reply_message])
26+
reply_messages = message_service.handle_audio_message(event)
27+
message_service.send_message(event.reply_token, reply_messages)

0 commit comments

Comments
 (0)