@rookiestar/eng-lang-tutor 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of @rookiestar/eng-lang-tutor might be problematic. Click here for more details.

@@ -0,0 +1,292 @@
1
+ # English Learning Resources
2
+
3
+ > This file contains themed resources for generating authentic American English content.
4
+ > LLM should reference these resources when generating knowledge points and quizzes.
5
+
6
+ ---
7
+
8
+ ## 1. Native American English Expressions
9
+
10
+ ### 1.1 Classic Learning Materials
11
+
12
+ | Resource | Description | Key Features |
13
+ |----------|-------------|--------------|
14
+ | **Speak English Like an American** | Classic textbook with 350+ idioms | Dialogue-based learning, everyday situations |
15
+ | **NTC's Dictionary of American Slang** | Comprehensive slang dictionary | Colloquial expressions, usage examples |
16
+ | **American Colloquial Expressions** | Collection of spoken phrases | Almost all idioms are colloquial language |
17
+
18
+ ### 1.2 Common Chinglish Corrections
19
+
20
+ | Chinglish (Wrong) | American (Correct) | Explanation |
21
+ |-------------------|-------------------|-------------|
22
+ | "It was just so-so." | "It was just ok/alright." | Americans rarely use "so-so" |
23
+ | "Let's go out to play." | "Let's go out!" | "Play" is for children; adults "go out" |
24
+ | "Bad for your health." | "It's not good for you." | More natural phrasing |
25
+ | "I very like it." | "I really like it." | "Very" doesn't modify "like" |
26
+ | "How to say?" | "How do I put this?" / "What's the word?" | Natural hesitation phrases |
27
+
28
+ ---
29
+
30
+ ## 2. TV Shows & Movies (Topic: movies)
31
+
32
+ ### 2.1 Recommended Shows for Learning
33
+
34
+ | Show | Genre | Learning Focus | Classic Lines |
35
+ |------|-------|----------------|---------------|
36
+ | **Friends** | Sitcom | Daily conversation, humor, sarcasm | "How you doin'?", "Oh. My. God!" |
37
+ | **The Office** | Workplace Comedy | Office idioms, corporate culture | "That's what she said", water-cooler chat |
38
+ | **Gossip Girl** | Teen Drama | Youth slang, fashion, social dynamics | "No offense." / "None taken.", "Done and done." |
39
+ | **Desperate Housewives** | Drama | Formal/informal switching, life situations | "Take a rain check", "Serves him right" |
40
+ | **House of Cards** | Political Thriller | Business/political vocabulary, formal speech | Complex sentence structures |
41
+ | **Modern Family** | Sitcom | Family dynamics, generational differences | "I'm not a regular mom, I'm a cool mom" |
42
+ | **How I Met Your Mother** | Sitcom | Dating slang, friendship expressions | "Legendary", "Suit up!" |
43
+
44
+ ### 2.2 Expression Examples from TV Shows
45
+
46
+ **From Friends:**
47
+ - "We were on a break!" - Defending oneself
48
+ - "Could I BE any more...?" - Emphasis (Chandler's style)
49
+ - "Pivot!" - When moving something difficult
50
+
51
+ **From The Office:**
52
+ - "Touch base" - Briefly connect with someone
53
+ - "Circle back" - Return to a topic later
54
+ - "Low-hanging fruit" - Easy wins
55
+
56
+ **From Gossip Girl:**
57
+ - "Any interest in fresh air?" - Casual invitation
58
+ - "You set me up!" - Accusing someone of a trap
59
+ - "It was a last minute thing." - Explaining spontaneity
60
+
61
+ ---
62
+
63
+ ## 3. News & Media (Topic: news)
64
+
65
+ ### 3.1 News Sources for Learning
66
+
67
+ | Source | Accent | Features | Topics |
68
+ |--------|--------|----------|--------|
69
+ | **CNN 10** | American | 10-min daily news for students | Current events, explained clearly |
70
+ | **VOA Learning English** | American | Slow pace, simplified vocabulary | World news, American life |
71
+ | **BBC Learning English** | British/American | Grammar focus, pronunciation | General English |
72
+ | **Engoo** | Various | Graded by difficulty, daily updates | News, culture, lifestyle |
73
+ | **NPR** | American | Natural pace, diverse topics | Culture, science, stories |
74
+
75
+ ### 3.2 News English Vocabulary
76
+
77
+ **Politics & Government:**
78
+ - bill, veto, legislation, incumbent, poll, constituency
79
+ - "The bill passed with a narrow margin."
80
+ - "The incumbent is seeking re-election."
81
+
82
+ **Business & Economy:**
83
+ - earnings, quarterly, forecast, market trends, inflation
84
+ - "Stocks rallied after the announcement."
85
+ - "The company exceeded earnings expectations."
86
+
87
+ **Technology:**
88
+ - breakthrough, innovation, cybersecurity, AI, startup
89
+ - "The startup secured Series B funding."
90
+ - "The breakthrough could revolutionize..."
91
+
92
+ ---
93
+
94
+ ## 4. Gaming (Topic: gaming)
95
+
96
+ ### 4.1 Gaming Terminology
97
+
98
+ | Category | Terms & Expressions |
99
+ |----------|-------------------|
100
+ | **Core Terms** | NPC, spawn, respawn, loot, grind, level up, XP, HP, MP |
101
+ | **Gameplay** | buff, nerf, patch, DLC, speedrun, playthrough |
102
+ | **Multiplayer** | party, squad, team up, PvP, PvE, co-op, matchmaking, lobby |
103
+ | **Competition** | GG (good game), noob, pro, clutch, carry, feed, throw, smurf |
104
+ | **Actions** | camp, kite, tank, heal, revive, reload, flank, rush |
105
+
106
+ ### 4.2 Gaming Expressions in Daily Use
107
+
108
+ | Expression | Meaning | Example |
109
+ |------------|---------|---------|
110
+ | "GG" | Good game, well done | "That presentation was GG." |
111
+ | "Nerf" | Weaken or reduce effectiveness | "They nerfed the office coffee machine." |
112
+ | "Grind" | Repetitive hard work | "I'm grinding through these reports." |
113
+ | "Carry" | Do most of the work | "She carried the whole project." |
114
+ | "Clutch" | Success under pressure | "That was a clutch presentation." |
115
+ | "Buff up" | Improve or strengthen | "Need to buff up my resume." |
116
+
117
+ ### 4.3 Gaming Dialogue Examples
118
+
119
+ ```
120
+ A: "Wanna squad up tonight?"
121
+ B: "Sure, I'll be on around 8."
122
+ A: "Cool, let's grind some ranked matches."
123
+ B: "Hopefully we don't get matched with noobs."
124
+ ```
125
+
126
+ ---
127
+
128
+ ## 5. Sports (Topic: sports)
129
+
130
+ ### 5.1 Basketball Vocabulary
131
+
132
+ | Category | Terms |
133
+ |----------|-------|
134
+ | **Scoring** | dunk, layup, three-pointer, free throw, buzzer beater |
135
+ | **Defense** | block, steal, rebound, turnover, foul |
136
+ | **Tactics** | pick and roll, fast break, zone defense, press |
137
+ | **Positions** | point guard, shooting guard, forward, center |
138
+
139
+ **Common Expressions:**
140
+ - "He hit a buzzer beater to win the game!"
141
+ - "She's on fire tonight!" (scoring consistently)
142
+ - "They're running away with this one." (winning easily)
143
+ - "It's a game of runs." (momentum shifts)
144
+
145
+ ### 5.2 Sports Idioms in Business/Daily Life
146
+
147
+ | Idiom | Meaning | Example |
148
+ |-------|---------|---------|
149
+ | "Step up to the plate" | Take responsibility | "It's time to step up to the plate." |
150
+ | "Ballpark figure" | Rough estimate | "Give me a ballpark figure." |
151
+ | "Drop the ball" | Make a mistake | "Don't drop the ball on this project." |
152
+ | "Hit it out of the park" | Do excellently | "She hit it out of the park with that pitch." |
153
+ | "On the ball" | Alert, competent | "He's really on the ball today." |
154
+ | "Full court press" | Intense effort | "We need a full court press on this deal." |
155
+
156
+ ---
157
+
158
+ ## 6. Workplace & Business (Topic: workplace)
159
+
160
+ ### 6.1 Office Idioms
161
+
162
+ | Expression | Meaning | Context |
163
+ |------------|---------|---------|
164
+ | "Touch base" | Briefly connect | "Let's touch base next week." |
165
+ | "Circle back" | Return to a topic | "Let's circle back on this later." |
166
+ | "Low-hanging fruit" | Easy wins | "Let's tackle the low-hanging fruit first." |
167
+ | "Bandwidth" | Capacity/availability | "I don't have the bandwidth for this." |
168
+ | "Synergy" | Combined effect | "Looking for synergy between teams." |
169
+ | "Pain point" | Problem area | "What are the customer pain points?" |
170
+ | "Value-add" | Benefit provided | "What's the value-add here?" |
171
+ | "Action items" | Tasks to complete | "Let's review the action items." |
172
+
173
+ ### 6.2 Meeting Phrases
174
+
175
+ **Starting:**
176
+ - "Let's get the ball rolling."
177
+ - "Shall we dive in?"
178
+ - "Thanks for hopping on this call."
179
+
180
+ **Contributing:**
181
+ - "I'd like to add something here."
182
+ - "Building on what [name] said..."
183
+ - "Here's my take on this."
184
+
185
+ **Disagreeing Politely:**
186
+ - "I see where you're coming from, but..."
187
+ - "That's an interesting perspective. Have we considered...?"
188
+ - "I'm not sure I entirely agree."
189
+
190
+ **Ending:**
191
+ - "Let's wrap this up."
192
+ - "To summarize, we've agreed on..."
193
+ - "I think we're good to go."
194
+
195
+ ### 6.3 Workplace Dialogue Examples
196
+
197
+ ```
198
+ A: "Hey, do you have a minute to touch base on the project?"
199
+ B: "Sure, what's up?"
200
+ A: "I wanted to circle back on the timeline. We might need more bandwidth."
201
+ B: "Got it. Let's identify the pain points and see what low-hanging fruit we can tackle first."
202
+ ```
203
+
204
+ ---
205
+
206
+ ## 7. Daily Life (Topic: daily_life)
207
+
208
+ ### 7.1 Shopping Expressions
209
+
210
+ | Situation | Expressions |
211
+ |-----------|-------------|
212
+ | **Asking for help** | "Can I help you?" / "Just looking, thanks." / "Where can I find...?" |
213
+ | **Asking about products** | "Do you have this in a different size/color?" / "Is this on sale?" |
214
+ | **Trying on** | "Can I try this on?" / "Where's the fitting room?" |
215
+ | **Price negotiation** | "Is this the best price?" / "Can you give me a discount?" |
216
+ | **At checkout** | "I'll take it." / "Do you take credit cards?" / "Can I get a receipt?" |
217
+
218
+ ### 7.2 Restaurant Expressions
219
+
220
+ | Situation | Expressions |
221
+ |-----------|-------------|
222
+ | **Ordering** | "I'd like the..." / "Could I get...?" / "What do you recommend?" |
223
+ | **Special requests** | "Could I get this without...?" / "Is this vegetarian?" |
224
+ | **During meal** | "Could I get some more water?" / "This is delicious!" |
225
+ | **Paying** | "Could I get the check, please?" / "Separate checks, please." |
226
+ | **To-go** | "Can I get this to go?" / "I'll have the rest to go." |
227
+
228
+ ### 7.3 Social Expressions
229
+
230
+ **Greetings:**
231
+ - "What's up?" / "How's it going?" / "How have you been?"
232
+ - "Long time no see!" / "Good to see you!"
233
+
234
+ **Casual Responses:**
235
+ - "Not much, you?" / "Can't complain." / "Same old, same old."
236
+ - "Take it easy!" / "Catch you later!" / "See ya!"
237
+
238
+ **Making Plans:**
239
+ - "We should hang out sometime." / "Let's grab coffee."
240
+ - "Are you free this weekend?" / "What are you up to tonight?"
241
+
242
+ **Polite Refusals:**
243
+ - "I'd love to, but I can't make it." / "Maybe another time."
244
+ - "I'll have to take a rain check."
245
+
246
+ ---
247
+
248
+ ## 8. Casual & Slang (All Topics)
249
+
250
+ ### 8.1 Common American Slang
251
+
252
+ | Slang | Meaning | Example |
253
+ |-------|---------|---------|
254
+ | "Gonna" | Going to | "I'm gonna grab some food." |
255
+ | "Gotta" | Have to | "I gotta run." |
256
+ | "Wanna" | Want to | "Do you wanna come?" |
257
+ | "Kinda" | Kind of | "It's kinda cold today." |
258
+ | "Lemme" | Let me | "Lemme see that." |
259
+ | "Dunno" | Don't know | "I dunno, maybe?" |
260
+ | "Ya" | You | "See ya later!" |
261
+ | "Y'all" | You all | "Y'all coming?" |
262
+
263
+ ### 8.2 Fillers and Hesitation
264
+
265
+ | Filler | Usage |
266
+ |--------|-------|
267
+ | "Like" | "It was, like, really expensive." |
268
+ | "You know" | "It's that place, you know, near the mall." |
269
+ | "I mean" | "I mean, I could try, but..." |
270
+ | "Sort of / Kind of" | "It's sort of complicated." |
271
+ | "Basically" | "Basically, what happened was..." |
272
+ | "Actually" | "Actually, I think you're right." |
273
+
274
+ ---
275
+
276
+ ## 9. Usage Notes for LLM
277
+
278
+ When generating content, reference these resources based on the selected topic:
279
+
280
+ 1. **Movies**: Use expressions from TV shows section
281
+ 2. **News**: Reference news vocabulary and formal expressions
282
+ 3. **Gaming**: Include gaming terminology and slang
283
+ 4. **Sports**: Use sports idioms and expressions
284
+ 5. **Workplace**: Focus on office idioms and meeting phrases
285
+ 6. **Social**: Use casual greetings and social expressions
286
+ 7. **Daily Life**: Reference shopping/restaurant dialogues
287
+
288
+ Always include:
289
+ - Scene context matching the topic
290
+ - Chinglish trap with correction
291
+ - Natural American alternatives
292
+ - Pronunciation tips for casual speech (gonna, gotta, etc.)
@@ -0,0 +1,9 @@
1
+ # Core dependencies
2
+ websocket-client>=1.6.0 # For XunFei TTS WebSocket API
3
+
4
+ # Optional: Edge-TTS support (uncomment if needed)
5
+ # edge-tts>=6.1.0
6
+
7
+ # Development dependencies
8
+ pytest>=7.0.0
9
+ pytest-asyncio>=0.21.0
@@ -0,0 +1,336 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Command Parser - Parses user messages and routes to appropriate handlers.
4
+
5
+ Handles:
6
+ - Command recognition via regex patterns
7
+ - Parameter extraction from messages
8
+ - Initialization flow detection
9
+ - Bilingual support (English/Chinese)
10
+ """
11
+
12
+ import re
13
+ from typing import Dict, Any, Optional, Tuple
14
+ from datetime import datetime, date, timedelta
15
+
16
+
17
+ class CommandParser:
18
+ """Parses user messages to determine intent and extract parameters."""
19
+
20
+ # Command patterns with bilingual support
21
+ COMMAND_PATTERNS = {
22
+ # Initialization commands
23
+ "init_start": r"(?i)^(start|begin|开始|初始化|你好|hello|hi|嗨|hey).*$",
24
+
25
+ # Keypoint commands
26
+ "keypoint_today": r"(?i)(keypoint|知识点|今天|today).*(?!.*(历史|history|昨天|yesterday|前天|上周))",
27
+ "keypoint_history": r"(?i)(keypoint|知识点).*(历史|history|昨天|yesterday|前天|previous|上周)",
28
+ "keypoint_date": r"(?i)(keypoint|知识点).*?(\d{4}-\d{2}-\d{2}|\d{1,2}月\d{1,2}[日号]?)",
29
+
30
+ # Quiz commands
31
+ "quiz_take": r"(?i)(quiz|测验|test|测试|答题|考试)",
32
+
33
+ # Stats commands
34
+ "stats_view": r"(?i)(stats|progress|进度|统计|等级|level|xp|连胜|streak|成就|achievement)",
35
+
36
+ # Config commands
37
+ "config_view": r"(?i)(config|settings?|设置|配置|偏好|preference)(?!.*(change|改|set|设|更新))",
38
+ "config_change_cefr": r"(?i)(cefr|等级|level).*(A1|A2|B1|B2|C1|C2)",
39
+ "config_change_style": r"(?i)(style|风格|导师).*(humorous|rigorous|casual|professional|幽默|严谨|随意|专业)",
40
+ "config_change_topics": r"(?i)(topic|主题|配比|权重|兴趣)",
41
+ "config_change_ratio": r"(?i)(ratio|比例).*(口语|oral|书面|written|speaking|writing)",
42
+
43
+ # Schedule commands
44
+ "schedule_view": r"(?i)(schedule|时间表|推送时间|定时)(?!.*(change|改|set|设))",
45
+ "schedule_change": r"(?i)(schedule|时间表|推送时间).*(change|改|set|设|调整)",
46
+
47
+ # Error notebook commands
48
+ "errors_view": r"(?i)(errors?|错题本|mistakes?|wrong|错误|错题)(?!.*(更多|more|随机|random|清空|clear|删除|remove|\d{4}-\d{2}))",
49
+ "errors_more": r"(?i)(errors?|错题本).*(更多|more|下一页|next)",
50
+ "errors_page": r"(?i)(errors?|错题本).*(第\s*\d+\s*页|page\s*\d+)",
51
+ "errors_month": r"(?i)(errors?|错题本).*(\d{4}-\d{2})(?!.*\d{2})", # YYYY-MM format
52
+ "errors_random": r"(?i)(errors?|错题本).*(随机|random)",
53
+ "errors_clear": r"(?i)(errors?|错题本).*(clear|清空|删除|remove)",
54
+ "errors_stats": r"(?i)(errors?|错题本).*(统计|stats|statistics)",
55
+ "errors_review": r"(?i)(errors?|错题本).*(复习|review|练习|practice)(?!.*(更多|more|随机|random|清空|clear|删除|remove|统计|stats))",
56
+
57
+ # Help command
58
+ "help": r"(?i)(help|帮助|usage|怎么用|how to use|command|命令|指令|功能)",
59
+ }
60
+
61
+ # Onboarding step patterns for collecting user input
62
+ ONBOARDING_PATTERNS = {
63
+ "cefr_level": r"(?i)(A1|A2|B1|B2|C1|C2)",
64
+ "tutor_style": r"(?i)(humorous|rigorous|casual|professional|幽默|严谨|随意|专业)",
65
+ "topics": r"(?i)(movies?|新闻?|news|游戏?|gaming|体育?|sports?|职场?|workplace|社交?|social|生活?|daily)",
66
+ "ratio": r"(\d{1,3})\s*(%|percent|百分比)?",
67
+ }
68
+
69
+ def __init__(self, state_manager=None):
70
+ """
71
+ Initialize the command parser.
72
+
73
+ Args:
74
+ state_manager: Optional StateManager instance for context
75
+ """
76
+ self.state_manager = state_manager
77
+
78
+ def parse(self, message: str, state: Dict[str, Any]) -> Dict[str, Any]:
79
+ """
80
+ Parse user message and return command info.
81
+
82
+ Args:
83
+ message: User's message text
84
+ state: Current state dictionary
85
+
86
+ Returns:
87
+ Dictionary with:
88
+ - command: Command name
89
+ - params: Extracted parameters
90
+ - requires_init: Whether command requires initialization
91
+ - onboarding_input: If in onboarding, the detected input type
92
+ """
93
+ # Check if user is in onboarding flow
94
+ if not state.get("initialized", False):
95
+ return self._handle_uninitialized(message, state)
96
+
97
+ # Parse for initialized users
98
+ return self._parse_command(message)
99
+
100
+ def _handle_uninitialized(self, message: str, state: Dict[str, Any]) -> Dict[str, Any]:
101
+ """Handle messages from uninitialized users."""
102
+ step = state.get("onboarding_step", 0)
103
+
104
+ if step == 0:
105
+ # Check for start command
106
+ if re.search(self.COMMAND_PATTERNS["init_start"], message):
107
+ return {
108
+ "command": "init_start",
109
+ "params": {},
110
+ "requires_init": False,
111
+ "onboarding_input": None
112
+ }
113
+ else:
114
+ # Any other message triggers welcome
115
+ return {
116
+ "command": "init_welcome",
117
+ "params": {},
118
+ "requires_init": False,
119
+ "onboarding_input": None
120
+ }
121
+
122
+ # User is in onboarding, detect what they're providing
123
+ onboarding_input = self._detect_onboarding_input(message, step)
124
+
125
+ return {
126
+ "command": "init_continue",
127
+ "params": {"step": step},
128
+ "requires_init": False,
129
+ "onboarding_input": onboarding_input
130
+ }
131
+
132
+ def _detect_onboarding_input(self, message: str, step: int) -> Optional[Dict[str, Any]]:
133
+ """Detect user input during onboarding based on current step."""
134
+ result = {"type": None, "value": None}
135
+
136
+ if step == 1: # CEFR level
137
+ match = re.search(self.ONBOARDING_PATTERNS["cefr_level"], message)
138
+ if match:
139
+ result = {"type": "cefr_level", "value": match.group(1).upper()}
140
+
141
+ elif step == 2: # Topics
142
+ topics = {}
143
+ topic_keywords = {
144
+ "movies": ["movie", "film", "影视", "电影", "美剧"],
145
+ "news": ["news", "新闻"],
146
+ "gaming": ["game", "gaming", "游戏"],
147
+ "sports": ["sport", "sports", "体育", "运动"],
148
+ "workplace": ["work", "workplace", "office", "职场", "工作"],
149
+ "social": ["social", "社交"],
150
+ "daily_life": ["daily", "life", "生活", "日常"]
151
+ }
152
+
153
+ for topic, keywords in topic_keywords.items():
154
+ for kw in keywords:
155
+ if kw.lower() in message.lower():
156
+ topics[topic] = 0.2 # Default weight
157
+ break
158
+
159
+ if topics:
160
+ # Normalize weights to sum to 1.0
161
+ total = sum(topics.values())
162
+ topics = {k: round(v / total, 2) for k, v in topics.items()}
163
+ result = {"type": "topics", "value": topics}
164
+
165
+ elif step == 3: # Tutor style
166
+ style_map = {
167
+ "humorous": ["humorous", "幽默"],
168
+ "rigorous": ["rigorous", "严谨"],
169
+ "casual": ["casual", "随意", "轻松"],
170
+ "professional": ["professional", "专业"]
171
+ }
172
+ for style, keywords in style_map.items():
173
+ for kw in keywords:
174
+ if kw.lower() in message.lower():
175
+ result = {"type": "tutor_style", "value": style}
176
+ break
177
+ if result["value"]:
178
+ break
179
+
180
+ elif step == 4: # Oral/written ratio
181
+ match = re.search(self.ONBOARDING_PATTERNS["ratio"], message)
182
+ if match:
183
+ ratio = int(match.group(1)) / 100.0
184
+ ratio = max(0, min(1, ratio)) # Clamp to 0-1
185
+ result = {"type": "oral_written_ratio", "value": ratio}
186
+
187
+ return result if result["type"] else None
188
+
189
+ def _parse_command(self, message: str) -> Dict[str, Any]:
190
+ """Parse message for an initialized user."""
191
+ result = {
192
+ "command": "unknown",
193
+ "params": {},
194
+ "requires_init": False,
195
+ "onboarding_input": None
196
+ }
197
+
198
+ # Check each command pattern
199
+ for cmd_name, pattern in self.COMMAND_PATTERNS.items():
200
+ match = re.search(pattern, message)
201
+ if match:
202
+ result["command"] = cmd_name
203
+ result["params"] = self._extract_params(cmd_name, match, message)
204
+ result["requires_init"] = not cmd_name.startswith("init") and cmd_name != "help"
205
+ break
206
+
207
+ return result
208
+
209
+ def _extract_params(self, cmd_name: str, match: re.Match, message: str) -> Dict[str, Any]:
210
+ """Extract parameters from matched command."""
211
+ params = {}
212
+
213
+ # Extract date from keypoint queries
214
+ if "date" in cmd_name or cmd_name in ["keypoint_today", "keypoint_history"]:
215
+ date_match = re.search(r"(\d{4}-\d{2}-\d{2})", message)
216
+ if date_match:
217
+ params["date"] = date_match.group(1)
218
+ elif "history" in cmd_name or "昨天" in message or "yesterday" in message.lower():
219
+ params["date"] = (date.today() - timedelta(days=1)).isoformat()
220
+ elif "前天" in message:
221
+ params["date"] = (date.today() - timedelta(days=2)).isoformat()
222
+ else:
223
+ params["date"] = date.today().isoformat()
224
+
225
+ # Extract CEFR level
226
+ if "cefr" in cmd_name:
227
+ level_match = re.search(r"(A1|A2|B1|B2|C1|C2)", message, re.I)
228
+ if level_match:
229
+ params["cefr_level"] = level_match.group(1).upper()
230
+
231
+ # Extract style
232
+ if "style" in cmd_name:
233
+ style_map = {
234
+ "humorous": "humorous", "幽默": "humorous",
235
+ "rigorous": "rigorous", "严谨": "rigorous",
236
+ "casual": "casual", "随意": "casual", "轻松": "casual",
237
+ "professional": "professional", "专业": "professional"
238
+ }
239
+ for keyword, style in style_map.items():
240
+ if keyword in message.lower():
241
+ params["tutor_style"] = style
242
+ break
243
+
244
+ # Extract ratio
245
+ if "ratio" in cmd_name:
246
+ ratio_match = re.search(r"(\d{1,3})\s*(%|percent|百分比)?", message)
247
+ if ratio_match:
248
+ params["oral_written_ratio"] = int(ratio_match.group(1)) / 100.0
249
+
250
+ # Extract error notebook pagination params
251
+ if cmd_name and cmd_name.startswith("errors"):
252
+ # Page number
253
+ page_match = re.search(r"(第\s*)?(\d+)(\s*页|page)", message, re.I)
254
+ if page_match:
255
+ params["page"] = int(page_match.group(2))
256
+
257
+ # Month filter (YYYY-MM)
258
+ month_match = re.search(r"(\d{4}-\d{2})(?!-\d{2})", message)
259
+ if month_match:
260
+ params["month"] = month_match.group(1)
261
+
262
+ # Random count
263
+ random_match = re.search(r"(随机|random)\s*(\d*)", message, re.I)
264
+ if random_match:
265
+ count = int(random_match.group(2)) if random_match.group(2) else 5
266
+ params["random"] = count
267
+
268
+ # Default page for "more" command
269
+ if "more" in cmd_name:
270
+ params["page"] = params.get("page", 2)
271
+
272
+ # Review count (for errors_review command)
273
+ if "review" in cmd_name:
274
+ count_match = re.search(r"(复习|review|练习|practice)\s*(\d*)", message, re.I)
275
+ if count_match and count_match.group(2):
276
+ params["count"] = int(count_match.group(2))
277
+ else:
278
+ params["count"] = 5 # Default 5 questions per session
279
+
280
+ return params
281
+
282
+ def get_command_suggestions(self, context: str = "general") -> list:
283
+ """
284
+ Get suggested commands based on context.
285
+
286
+ Args:
287
+ context: Context for suggestions (general, after_quiz, morning, etc.)
288
+
289
+ Returns:
290
+ List of suggested command strings
291
+ """
292
+ suggestions = {
293
+ "general": ["keypoint", "quiz", "stats", "help"],
294
+ "after_quiz": ["stats", "errors", "keypoint"],
295
+ "morning": ["keypoint", "quiz"],
296
+ "evening": ["quiz", "stats"]
297
+ }
298
+ return suggestions.get(context, suggestions["general"])
299
+
300
+
301
+ # CLI interface for testing
302
+ if __name__ == "__main__":
303
+ import argparse
304
+ import json
305
+
306
+ parser = argparse.ArgumentParser(description="Command Parser for eng-lang-tutor")
307
+ parser.add_argument('--message', type=str, help='Message to parse')
308
+ parser.add_argument('--demo', action='store_true', help='Run demo')
309
+
310
+ args = parser.parse_args()
311
+
312
+ cp = CommandParser()
313
+
314
+ if args.demo:
315
+ test_messages = [
316
+ "start",
317
+ "今天知识点",
318
+ "keypoint today",
319
+ "quiz",
320
+ "我的进度",
321
+ "stats",
322
+ "config",
323
+ "设置 CEFR 为 B2",
324
+ "help",
325
+ "错题本"
326
+ ]
327
+
328
+ print("=== Command Parser Demo ===\n")
329
+ for msg in test_messages:
330
+ result = cp._parse_command(msg)
331
+ print(f"Message: {msg}")
332
+ print(f"Result: {json.dumps(result, indent=2)}\n")
333
+
334
+ elif args.message:
335
+ result = cp._parse_command(args.message)
336
+ print(json.dumps(result, indent=2, ensure_ascii=False))