-
Notifications
You must be signed in to change notification settings - Fork 6
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Optimize Tarot card drawing logic for daily user draws #98
base: v3
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,57 +1,75 @@ | ||
import json | ||
import os | ||
import random | ||
from datetime import datetime | ||
from pathlib import Path | ||
from typing import Dict, Tuple | ||
|
||
from graia.ariadne.app import Ariadne | ||
from graia.ariadne.event.message import Group, GroupMessage | ||
from graia.ariadne.event.message import Group, GroupMessage, Member | ||
from graia.ariadne.message.chain import MessageChain | ||
from graia.ariadne.message.element import Plain, Image, Source | ||
from graia.ariadne.message.parser.twilight import Twilight, FullMatch, SpacePolicy | ||
from graia.ariadne.util.saya import listen, decorate, dispatch | ||
from graia.saya import Saya, Channel | ||
|
||
from core.control import ( | ||
Permission, | ||
Function, | ||
FrequencyLimitation, | ||
Distribute | ||
) | ||
from core.control import Permission, Function, FrequencyLimitation, Distribute | ||
from core.models import saya_model | ||
|
||
module_controller = saya_model.get_module_controller() | ||
saya = Saya.current() | ||
channel = Channel.current() | ||
channel.meta["name"] = ("Tarot") | ||
channel.meta["author"] = ("SAGIRI-kawaii") | ||
channel.meta["description"] = ("可以抽塔罗牌的插件,在群中发送 `-塔罗牌` 即可") | ||
channel.meta["name"] = "Tarot" | ||
channel.meta["author"] = "SAGIRI-kawaii" | ||
channel.meta["description"] = "可以抽塔罗牌的插件,在群中发送 `-塔罗牌` 即可" | ||
channel.metadata = module_controller.get_metadata_from_path(Path(__file__)) | ||
|
||
|
||
@listen(GroupMessage) | ||
@dispatch( | ||
Twilight( | ||
[ | ||
FullMatch("-塔罗牌").space(SpacePolicy.PRESERVE) | ||
] | ||
) | ||
) | ||
@dispatch(Twilight([FullMatch("-塔罗牌").space(SpacePolicy.PRESERVE)])) | ||
@decorate( | ||
Distribute.require(), | ||
Function.require(channel.module), | ||
FrequencyLimitation.require(channel.module), | ||
Permission.group_require(channel.metadata.level), | ||
Permission.user_require(Permission.User), | ||
) | ||
async def tarot(app: Ariadne, group: Group, source: Source): | ||
await app.send_group_message(group, Tarot.get_tarot(), quote=source) | ||
async def tarot(app: Ariadne, group: Group, sender: Member, source: Source): | ||
await app.send_group_message(group, await Tarot.get_tarot(sender.id), quote=source) | ||
|
||
|
||
class Tarot(object): | ||
# Store user daily results: {user_id: (date, card_info, filename)} | ||
_user_draws: Dict[int, Tuple[str, dict, str]] = {} | ||
|
||
@staticmethod | ||
def get_tarot() -> MessageChain: | ||
async def get_tarot(user_id: int) -> MessageChain: | ||
today = datetime.now().strftime("%Y-%m-%d") | ||
|
||
# Check if user already drew today | ||
if user_id in Tarot._user_draws: | ||
last_date, card, filename = Tarot._user_draws[user_id] | ||
if last_date == today: | ||
card_dir = ( | ||
"normal" | ||
if card["meaning"]["normal"] in card["meaning"].get("current", "") | ||
else "reverse" | ||
) | ||
card_type = "正位" if card_dir == "normal" else "逆位" | ||
content = f"[重复] {card['name']} ({card['name-en']}) {card_type}\n牌意:{card['meaning'][card_dir]}" | ||
elements = [] | ||
img_path = f"{os.getcwd()}/statics/tarot/{card_dir}/{filename}.jpg" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. suggestion: 考虑使用 pathlib 构建跨平台文件路径。 在所有平台上,将 os.getcwd() 与字符串连接使用可能并不稳健。 考虑使用 Path(file).parent / 'statics' / 'tarot' / card_dir / (filename + '.jpg') 构建文件路径。 建议的实施方式: import os
from pathlib import Path img_path = str(Path(__file__).parent / 'statics' / 'tarot' / card_dir / (filename + '.jpg')) Original comment in Englishsuggestion: Consider using pathlib for cross-platform file path construction. Using os.getcwd() with string concatenation may not be robust on all platforms. Consider using Path(file).parent / 'statics' / 'tarot' / card_dir / (filename + '.jpg') to construct the file path. Suggested implementation: import os
from pathlib import Path img_path = str(Path(__file__).parent / 'statics' / 'tarot' / card_dir / (filename + '.jpg'))
Comment on lines
+52
to
+61
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. suggestion: 考虑将卡片方向显式存储在缓存中。 目前,重复的分支通过检查 card['meaning']['normal'] 是否在 card['meaning'].get('current', '') 中来推断卡片方向。 如果卡片文本发生更改,这种间接推断可能会容易出错。 将所选方向与卡片详细信息一起存储可以简化逻辑并提高未来的可维护性。 建议的实施方式: _user_draws: Dict[int, Tuple[str, dict, str, str]] = {} # Check if user already drew today
if user_id in Tarot._user_draws:
last_date, card, filename, card_dir = Tarot._user_draws[user_id]
if last_date == today:
card_type = "正位" if card_dir == "normal" else "逆位"
content = f"[重复] {card['name']} ({card['name-en']}) {card_type}\n牌意:{card['meaning'][card_dir]}"
elements = []
img_path = f"{os.getcwd()}/statics/tarot/{card_dir}/{filename}.jpg"
if filename and os.path.exists(img_path):
elements.append(Image(path=img_path))
elements.append(Plain(text=content))
return MessageChain(elements) # Draw new card
card, filename = Tarot.get_random_tarot()
card_dir = random.choice(["normal", "reverse"])
card["meaning"]["current"] = card["meaning"][card_dir] # Store current meaning
Tarot._user_draws[user_id] = (today, card, filename, card_dir) 确保相应地更新代码中依赖于 _user_draws 结构的任何其他部分。 Original comment in Englishsuggestion: Consider storing the card orientation explicitly in the cache. Currently, the repeated branch infers the card orientation by checking if card['meaning']['normal'] is in card['meaning'].get('current', ''). This indirect inference may be error prone if card texts change. Storing the chosen orientation alongside the card details could simplify the logic and improve future maintainability. Suggested implementation: _user_draws: Dict[int, Tuple[str, dict, str, str]] = {} # Check if user already drew today
if user_id in Tarot._user_draws:
last_date, card, filename, card_dir = Tarot._user_draws[user_id]
if last_date == today:
card_type = "正位" if card_dir == "normal" else "逆位"
content = f"[重复] {card['name']} ({card['name-en']}) {card_type}\n牌意:{card['meaning'][card_dir]}"
elements = []
img_path = f"{os.getcwd()}/statics/tarot/{card_dir}/{filename}.jpg"
if filename and os.path.exists(img_path):
elements.append(Image(path=img_path))
elements.append(Plain(text=content))
return MessageChain(elements) # Draw new card
card, filename = Tarot.get_random_tarot()
card_dir = random.choice(["normal", "reverse"])
card["meaning"]["current"] = card["meaning"][card_dir] # Store current meaning
Tarot._user_draws[user_id] = (today, card, filename, card_dir) Ensure that any other parts of the code that rely on the structure of _user_draws are updated accordingly. |
||
if filename and os.path.exists(img_path): | ||
elements.append(Image(path=img_path)) | ||
elements.append(Plain(text=content)) | ||
return MessageChain(elements) | ||
|
||
# Draw new card | ||
card, filename = Tarot.get_random_tarot() | ||
card_dir = random.choice(["normal", "reverse"]) | ||
card["meaning"]["current"] = card["meaning"][card_dir] # Store current meaning | ||
Tarot._user_draws[user_id] = (today, card, filename) | ||
|
||
card_type = "正位" if card_dir == "normal" else "逆位" | ||
content = f"{card['name']} ({card['name-en']}) {card_type}\n牌意:{card['meaning'][card_dir]}" | ||
elements = [] | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
question (bug_risk): 审查异步上下文中 _user_draws 的潜在并发问题。
由于 _user_draws 是从异步函数访问的类级别可变字典,请考虑并发访问是否可能导致竞争条件。 如果此代码并发执行,则简单的锁定机制或异步安全数据结构可能会有所帮助。
Original comment in English
question (bug_risk): Review potential concurrency issues with _user_draws in an async context.
Since _user_draws is a class-level mutable dictionary accessed from an async function, consider whether concurrent access might lead to race conditions. A simple locking mechanism or an async-safe data structure could help if this code is executed concurrently.