Skip to content

Commit d03d052

Browse files
authored
User setting (#12)
* refactor: use SQLAlchemy Mapped and mapped_column for type hints The SQLAlchemy models now use `Mapped` and `mapped_column` for defining columns, which provides better type hinting and improves code readability. * fix: welcome message quickreply error * feat: add roles, accpted_terms, and language fields to User model * feat(user_repository.py): add methods to update user attributes * feat: user data updates on postback events The typo in the `update__accpted_terms` method name in `user_repository.py` has been corrected to `update_accpted_terms`. The `handle_postback_event` function in `postback.py` now updates user data (accepted terms, language, role) in the database based on the postback data reeived. The `WelcomeService` now validates the reply token to prevent errors when the token is missing. * docs: add log to user setting * feat: handle unknown postback event * feat: handle unhandled postback events * feat: add clear_conversation_id and reset_user postback handlers
1 parent 217cf5d commit d03d052

7 files changed

Lines changed: 204 additions & 81 deletions

File tree

linebot/app/models/user.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# use sqlite
2-
from sqlalchemy import Column, Integer, String
2+
from sqlalchemy import String
3+
from sqlalchemy.orm import Mapped, mapped_column
34
from app.db.database import Base
45
from dataclasses import dataclass
56
from typing import Optional
@@ -8,8 +9,11 @@
89
class User(Base):
910
__tablename__ = "users"
1011

11-
line_id = Column(String, primary_key=True, index=True)
12-
conversation_id = Column(String, nullable=True)
12+
line_id: Mapped[str] = mapped_column(String, primary_key=True, index=True)
13+
conversation_id: Mapped[Optional[str]] = mapped_column(String, nullable=True)
14+
roles: Mapped[str] = mapped_column(String, default="role_student")
15+
accpted_terms: Mapped[bool] = mapped_column(default=False)
16+
language: Mapped[str] = mapped_column(String, default="zh-TW")
1317

1418
def __repr__(self):
1519
return f"<User line_id={self.line_id}>"

linebot/app/repositories/user_repository.py

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
from sqlalchemy.orm import Session
21
from ..models.user import User
32
from ..db.database import SessionLocal
43

@@ -23,6 +22,24 @@ def update_conversation_id(self, line_id: str, conversation_id: str) -> None:
2322
user.conversation_id = conversation_id
2423
self.db.commit()
2524

25+
def update_accpted_terms(self, line_id: str, accepted: bool) -> None:
26+
"""更新用戶是否接受服務條款"""
27+
user = self.get_user(line_id)
28+
user.accpted_terms = accepted
29+
self.db.commit()
30+
31+
def update_language(self, line_id: str, language: str) -> None:
32+
"""更新用戶的語言設定"""
33+
user = self.get_user(line_id)
34+
user.language = language
35+
self.db.commit()
36+
37+
def update_roles(self, line_id: str, roles: str) -> None:
38+
"""更新用戶的角色"""
39+
user = self.get_user(line_id)
40+
user.roles = roles
41+
self.db.commit()
42+
2643
def __del__(self):
2744
"""確保資料庫連接被正確關閉"""
2845
self.db.close()

linebot/app/routers/linebot.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,9 @@ def handle_follow_event(event):
6060
def handle_postback_event(event):
6161
logger.info("收到按鈕事件")
6262
reply_messages = postback_service.handle_postback_event(event)
63+
if not reply_messages:
64+
logger.warning("未知的 postback event,可能是換 rich menu 頁面")
65+
return
6366
postback_service.send_message(event.reply_token, reply_messages)
6467

6568

linebot/app/services/handlers/postback.py

Lines changed: 45 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,12 @@
66
MessageAction,
77
)
88
from ...config.logger import get_logger
9+
from ...config.line_config import line_bot_api
910
from ..utils import flex_message_convert_to_json
11+
from ...repositories.user_repository import UserRepository
1012

1113
logger = get_logger(__name__)
14+
user_repository = UserRepository()
1215

1316
WELCOME_MESSAGE_AFTER_SETTING = """👋 嗨!歡迎使用「成大 Linebot」🌳✨
1417
無論是校園資訊、活動查詢、選課資訊還是校內生活大小事,我都可以為你服務!
@@ -33,7 +36,11 @@ def create_quickreply():
3336

3437
def handle_postback_event(event):
3538
data = event.postback.data
39+
user_id = event.source.user_id
40+
user_profile = line_bot_api.get_profile(user_id)
41+
user_display_name = user_profile.display_name
3642
if data == "read_terms":
43+
logger.info(f"User {user_id} requested to read terms.")
3744
return [
3845
TextSendMessage(text=f"{TERMS_MESSAGE}"),
3946
FlexSendMessage(
@@ -44,6 +51,8 @@ def handle_postback_event(event):
4451
),
4552
]
4653
elif data == "accept_terms":
54+
logger.info(f"User {user_id} accepted terms.")
55+
user_repository.update_accpted_terms(user_id, True)
4756
return [
4857
TextSendMessage(text="感謝您的回覆,接下來我們來設定您的個人資料吧!"),
4958
FlexSendMessage(
@@ -54,8 +63,11 @@ def handle_postback_event(event):
5463
),
5564
]
5665
elif data == "reject_terms":
66+
logger.info(f"User {user_id} rejected terms.")
67+
user_repository.update_accpted_terms(user_id, False)
5768
return [TextSendMessage(text="感謝您的回覆,如果有需要隨時可以點擊同意歐")]
5869
elif data == "zh-TW":
70+
user_repository.update_language(user_id, "zh-TW")
5971
return [
6072
TextSendMessage(text="感謝您的回覆,接下來我們來設定您的個人資料吧!"),
6173
FlexSendMessage(
@@ -66,6 +78,8 @@ def handle_postback_event(event):
6678
),
6779
]
6880
elif data == "en":
81+
logger.info(f"User {user_id} selected English language.")
82+
user_repository.update_language(user_id, "en")
6983
return [
7084
TextSendMessage(
7185
text="Thank you for your reply, let's set up your profile next!"
@@ -78,27 +92,55 @@ def handle_postback_event(event):
7892
),
7993
]
8094
elif data == "role_faculty":
81-
welcome_test_message = "hahahaha"
95+
logger.info(f"User {user_id} selected faculty role.")
96+
user_repository.update_roles(user_id, "faculty")
8297
return [
8398
TextSendMessage(text="您已經設定為教職員身份,鵝子歡迎您!"),
8499
TextSendMessage(
85100
text=WELCOME_MESSAGE_AFTER_SETTING, quick_reply=create_quickreply()
86101
),
87102
]
88103
elif data == "role_student":
104+
logger.info(f"User {user_id} selected student role.")
105+
user_repository.update_roles(user_id, "student")
89106
return [
90107
TextSendMessage(text="您已經設定為學生身份,鵝子歡迎您!"),
91108
TextSendMessage(
92109
text=WELCOME_MESSAGE_AFTER_SETTING, quick_reply=create_quickreply()
93110
),
94-
create_quickreply(),
95111
]
96112
elif data == "role_visitor":
113+
logger.info(f"User {user_id} selected visitor role.")
114+
user_repository.update_roles(user_id, "visitor")
97115
return [
98116
TextSendMessage(text="您已經設定為校外人士身份"),
99117
TextSendMessage(
100118
text=WELCOME_MESSAGE_AFTER_SETTING, quick_reply=create_quickreply()
101119
),
102120
]
121+
elif data == "clear_conversation_id":
122+
logger.info(f"User {user_id} requested to clear conversation ID.")
123+
return_value = user_repository.update_conversation_id(user_id, "")
124+
logger.info(f"Conversation ID cleared for user {user_id}: {return_value}")
125+
return [
126+
TextSendMessage(
127+
text=f"逼..嗶茲..!@清除對話紀錄成功,雖然我忘了這段時間和{user_display_name}的點點滴滴,不過我還是期待和你重新開始吧!"
128+
)
129+
]
130+
elif data == "reset_user":
131+
logger.info(f"User {user_id} requested to reset user data.")
132+
user_repository.update_conversation_id(user_id, "")
133+
user_repository.update_accpted_terms(user_id, False)
134+
user_repository.update_language(user_id, "zh-TW")
135+
user_repository.update_roles(user_id, "visitor")
136+
return [
137+
TextSendMessage(text=f"已清空{user_display_name}的資料,請開始設定吧"),
138+
FlexSendMessage(
139+
alt_text="請閱讀服務條款",
140+
contents=flex_message_convert_to_json(
141+
"flex_messages/welcome/terms.json"
142+
),
143+
),
144+
]
103145
else:
104-
return [TextSendMessage(text="請選擇一個選項")]
146+
logger.warning(f"Unknown postback data: {data} from user {user_id}")

linebot/app/services/postback_service.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,12 @@ class PostbackService:
1515

1616
def handle_postback_event(self, event):
1717
"""處理按鈕事件"""
18+
result = handle_postback_event(event)
19+
if not result:
20+
logger.warning(f"未處理的按鈕事件: {event.postback.data}")
21+
return None
1822
show_loading_animation(event.source.user_id)
19-
return handle_postback_event(event)
23+
return result
2024

2125
def send_message(self, reply_token, messages):
2226
"""發送訊息到 LINE"""

linebot/app/services/welcome_service.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,11 @@ def send_welcome_message(self, event: FollowEvent):
3131
if event.type != "follow":
3232
self.logger.info(f"Not a follow event: {event}")
3333
return
34+
reply_token = event.reply_token
35+
36+
if not reply_token:
37+
self.logger.warning("No reply token found in the event.")
38+
return
3439

3540
user_id = event.source.user_id
3641
user_profile = self.line_bot_api.get_profile(user_id)
@@ -42,7 +47,7 @@ def send_welcome_message(self, event: FollowEvent):
4247
self.logger.info(f"Sending welcome message to user: {user_id}")
4348
flex_message_json = flex_message_convert_to_json("flex_messages/welcome/1.json")
4449
send_message(
45-
event.reply_token,
50+
reply_token,
4651
[
4752
TextSendMessage(
4853
text=WELCOME_MESSAGE_1.format(user_display_name=user_display_name)

0 commit comments

Comments
 (0)