@rookiestar/eng-lang-tutor 1.0.1 → 1.2.4

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.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,57 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file.
4
4
 
5
+ ## [1.2.4] - 2026-02-27
6
+
7
+ ### Fixed
8
+ - **TTS voice selection**: Audio composer now automatically selects appropriate voices based on TTS provider
9
+ - Edge-TTS: `en-US-JennyNeural`, `en-US-EricNeural`
10
+ - XunFei: `catherine`, `henry`
11
+ - **CLI import paths**: Fixed broken imports in `state_manager.py` CLI module
12
+
13
+ ### Changed
14
+ - **npm package**: Removed automatic `pip install` from postinstall script
15
+ - Added `eng-lang-tutor-setup` CLI tool for manual dependency installation
16
+ - Supports `--venv`, `--user`, and `--check` options
17
+ - **Test coverage**: Added 38 new tests covering:
18
+ - `scripts/audio/utils.py` (6 tests)
19
+ - `scripts/audio/converter.py` (16 tests)
20
+ - `scripts/utils/helpers.py` (16 tests)
21
+ - **Test fixes**: Fixed pre-existing test failures in CLI and TTS modules
22
+
23
+ ## [1.2.3] - 2026-02-27
24
+
25
+ ### Changed
26
+ - Version bump for npm publishing
27
+
28
+ ## [1.2.0] - 2026-02-27
29
+
30
+ ### Changed
31
+ - Initial npm package setup
32
+
33
+ ## [1.0.1] - 2025-02-27
34
+
35
+ ### Fixed
36
+ - **TTS voice selection**: Audio composer now automatically selects appropriate voices based on TTS provider
37
+ - Edge-TTS: `en-US-JennyNeural`, `en-US-EricNeural`
38
+ - XunFei: `catherine`, `henry`
39
+ - **CLI import paths**: Fixed broken imports in `state_manager.py` CLI module
40
+
41
+ ### Changed
42
+ - **npm package**: Removed automatic `pip install` from postinstall script
43
+ - Added `eng-lang-tutor-setup` CLI tool for manual dependency installation
44
+ - Supports `--venv`, `--user`, and `--check` options
45
+ - **Test coverage**: Added 38 new tests covering:
46
+ - `scripts/audio/utils.py` (6 tests)
47
+ - `scripts/audio/converter.py` (16 tests)
48
+ - `scripts/utils/helpers.py` (16 tests)
49
+ - **Test fixes**: Fixed pre-existing test failures in CLI and TTS modules
50
+
51
+ ## [1.0.1] - 2025-02-27
52
+
53
+ ### Changed
54
+ - Version bump for npm publishing (1.0.0 was previously unpublished)
55
+
5
56
  ## [1.0.0] - 2025-02-26
6
57
 
7
58
  Initial release.
package/README.md CHANGED
@@ -37,10 +37,21 @@ sudo apt-get install ffmpeg python3 python3-venv
37
37
  **方式一:npm 安装(推荐)**
38
38
 
39
39
  ```bash
40
+ # 1. Install the npm package
40
41
  npm install -g @rookiestar/eng-lang-tutor
41
- ```
42
42
 
43
- 安装会自动执行,skill 将被安装到 `~/.openclaw/skills/eng-lang-tutor/`。
43
+ # 2. Install Python dependencies (choose one method)
44
+
45
+ # Option A: Using virtual environment (recommended)
46
+ eng-lang-tutor-setup --venv
47
+ # Then activate before using: source ~/.venvs/eng-lang-tutor/bin/activate
48
+
49
+ # Option B: Install to user directory
50
+ eng-lang-tutor-setup --user
51
+
52
+ # Option C: Check if dependencies are already installed
53
+ eng-lang-tutor-setup --check
54
+ ```
44
55
 
45
56
  **方式二:从源码安装**
46
57
 
@@ -57,12 +68,13 @@ pip install -r eng-lang-tutor/requirements.txt
57
68
  **手动安装依赖(如需要):**
58
69
 
59
70
  ```bash
60
- # 创建 Python 虚拟环境
71
+ # 使用 setup 脚本(推荐)
72
+ eng-lang-tutor-setup --venv ~/.venvs/eng-lang-tutor
73
+
74
+ # 或手动创建虚拟环境
61
75
  python3 -m venv ~/.venvs/eng-lang-tutor
62
76
  source ~/.venvs/eng-lang-tutor/bin/activate # Linux/macOS
63
-
64
- # 安装依赖
65
- pip install -r ~/.openclaw/skills/eng-lang-tutor/requirements.txt
77
+ pip install -r $(npm root -g)/@rookiestar/eng-lang-tutor/requirements.txt
66
78
  ```
67
79
 
68
80
  **验证安装:**
@@ -265,6 +277,7 @@ eng-lang-tutor/
265
277
  │ ├── cli/ # 命令行模块
266
278
  │ │ ├── cli.py # CLI 入口点
267
279
  │ │ └── command_parser.py # 用户命令解析
280
+ │ ├── setup.py # 依赖安装脚本 (npm bin)
268
281
  │ ├── scheduling/ # 调度模块
269
282
  │ │ └── cron_push.py # 定时内容推送
270
283
  │ ├── utils/ # 工具模块
@@ -0,0 +1,47 @@
1
+ {
2
+ "version": 2,
3
+ "initialized": false,
4
+ "onboarding_step": 0,
5
+ "completion_status": {
6
+ "quiz_completed_date": null,
7
+ "keypoint_view_history": []
8
+ },
9
+ "schedule": {
10
+ "keypoint_time": "06:45",
11
+ "quiz_time": "22:45",
12
+ "timezone": "Asia/Shanghai"
13
+ },
14
+ "user": {
15
+ "xp": 0,
16
+ "level": 1,
17
+ "streak": 0,
18
+ "streak_freeze": 0,
19
+ "gems": 0,
20
+ "badges": []
21
+ },
22
+ "preferences": {
23
+ "cefr_level": "B1",
24
+ "oral_written_ratio": 0.7,
25
+ "topics": {
26
+ "movies": 0.2,
27
+ "news": 0.15,
28
+ "gaming": 0.15,
29
+ "sports": 0.1,
30
+ "workplace": 0.2,
31
+ "social": 0.1,
32
+ "daily_life": 0.1
33
+ },
34
+ "tutor_style": "humorous",
35
+ "dedup_days": 14
36
+ },
37
+ "progress": {
38
+ "total_quizzes": 0,
39
+ "correct_rate": 0.0,
40
+ "last_study_date": null,
41
+ "perfect_quizzes": 0,
42
+ "expressions_learned": 0
43
+ },
44
+ "recent_topics": [],
45
+ "error_notebook": [],
46
+ "error_archive": []
47
+ }
@@ -0,0 +1,47 @@
1
+ {
2
+ "version": 2,
3
+ "initialized": false,
4
+ "onboarding_step": 0,
5
+ "completion_status": {
6
+ "quiz_completed_date": null,
7
+ "keypoint_view_history": []
8
+ },
9
+ "schedule": {
10
+ "keypoint_time": "06:45",
11
+ "quiz_time": "22:45",
12
+ "timezone": "Asia/Shanghai"
13
+ },
14
+ "user": {
15
+ "xp": 0,
16
+ "level": 1,
17
+ "streak": 0,
18
+ "streak_freeze": 0,
19
+ "gems": 0,
20
+ "badges": []
21
+ },
22
+ "preferences": {
23
+ "cefr_level": "B1",
24
+ "oral_written_ratio": 0.7,
25
+ "topics": {
26
+ "movies": 0.2,
27
+ "news": 0.15,
28
+ "gaming": 0.15,
29
+ "sports": 0.1,
30
+ "workplace": 0.2,
31
+ "social": 0.1,
32
+ "daily_life": 0.1
33
+ },
34
+ "tutor_style": "humorous",
35
+ "dedup_days": 14
36
+ },
37
+ "progress": {
38
+ "total_quizzes": 0,
39
+ "correct_rate": 0.0,
40
+ "last_study_date": null,
41
+ "perfect_quizzes": 0,
42
+ "expressions_learned": 0
43
+ },
44
+ "recent_topics": [],
45
+ "error_notebook": [],
46
+ "error_archive": []
47
+ }
@@ -0,0 +1,47 @@
1
+ {
2
+ "version": 2,
3
+ "initialized": false,
4
+ "onboarding_step": 0,
5
+ "completion_status": {
6
+ "quiz_completed_date": null,
7
+ "keypoint_view_history": []
8
+ },
9
+ "schedule": {
10
+ "keypoint_time": "06:45",
11
+ "quiz_time": "22:45",
12
+ "timezone": "Asia/Shanghai"
13
+ },
14
+ "user": {
15
+ "xp": 0,
16
+ "level": 1,
17
+ "streak": 0,
18
+ "streak_freeze": 0,
19
+ "gems": 0,
20
+ "badges": []
21
+ },
22
+ "preferences": {
23
+ "cefr_level": "B1",
24
+ "oral_written_ratio": 0.7,
25
+ "topics": {
26
+ "movies": 0.2,
27
+ "news": 0.15,
28
+ "gaming": 0.15,
29
+ "sports": 0.1,
30
+ "workplace": 0.2,
31
+ "social": 0.1,
32
+ "daily_life": 0.1
33
+ },
34
+ "tutor_style": "humorous",
35
+ "dedup_days": 14
36
+ },
37
+ "progress": {
38
+ "total_quizzes": 0,
39
+ "correct_rate": 0.0,
40
+ "last_study_date": null,
41
+ "perfect_quizzes": 0,
42
+ "expressions_learned": 0
43
+ },
44
+ "recent_topics": [],
45
+ "error_notebook": [],
46
+ "error_archive": []
47
+ }
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "@rookiestar/eng-lang-tutor",
3
- "version": "1.0.1",
3
+ "version": "1.2.4",
4
4
  "description": "地道美式英语导师 - OpenClaw Skill for learning authentic American English",
5
5
  "main": "scripts/cli/cli.py",
6
- "scripts": {
7
- "postinstall": "python3 -m pip install -r requirements.txt --quiet"
6
+ "bin": {
7
+ "eng-lang-tutor-setup": "scripts/setup.py"
8
8
  },
9
9
  "keywords": [
10
10
  "english",
@@ -87,9 +87,9 @@ class AudioComposer:
87
87
  lead_in_silence: float = 1.0, # 引导语后留白
88
88
  section_silence: float = 2.0, # 内容后留白(段落间隔)
89
89
  dialogue_silence: float = 0.5, # 对话行之间留白
90
- narrator_voice: str = "henry", # 旁白音色(男声)
91
- voice_a: str = "mary", # 对话 A 音色(女声)
92
- voice_b: str = "henry", # 对话 B 音色(男声,沉稳)
90
+ narrator_voice: str = None, # 旁白音色(None 时使用 TTS provider 默认值)
91
+ voice_a: str = None, # 对话 A 音色(None 时使用 TTS provider 默认值)
92
+ voice_b: str = None, # 对话 B 音色(None 时使用 TTS provider 默认值)
93
93
  speed: float = 0.9 # 语速
94
94
  ) -> CompositionResult:
95
95
  """
@@ -110,6 +110,11 @@ class AudioComposer:
110
110
  CompositionResult: 合成结果
111
111
  """
112
112
  try:
113
+ # 使用 TTS provider 的默认音色(自动适配 Edge-TTS 或讯飞)
114
+ narrator_voice = narrator_voice or self.tts.get_voice_by_role("narrator")
115
+ voice_a = voice_a or self.tts.get_voice_by_role("dialogue_a")
116
+ voice_b = voice_b or self.tts.get_voice_by_role("dialogue_b")
117
+
113
118
  output_path = Path(output_path)
114
119
  output_path.parent.mkdir(parents=True, exist_ok=True)
115
120
 
@@ -213,7 +218,7 @@ class AudioComposer:
213
218
  if not text:
214
219
  continue
215
220
 
216
- # A = henry (男声), B = catherine (女声)
221
+ # A = EricNeural (男声), B = JennyNeural (女声)
217
222
  voice = voice_a if speaker.upper() == "A" else voice_b
218
223
 
219
224
  segment = self._synthesize_segment(
@@ -810,5 +810,8 @@ class StateManager:
810
810
 
811
811
  # CLI interface - delegated to cli.py for better code organization
812
812
  if __name__ == "__main__":
813
- from cli import main
813
+ try:
814
+ from ..cli.cli import main
815
+ except ImportError:
816
+ from scripts.cli.cli import main
814
817
  main()
@@ -0,0 +1,160 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Setup script for eng-lang-tutor skill.
4
+ Installs Python dependencies with support for virtual environments.
5
+
6
+ Usage:
7
+ eng-lang-tutor-setup # Install dependencies
8
+ eng-lang-tutor-setup --venv # Create venv and install
9
+ eng-lang-tutor-setup --check # Check dependencies only
10
+ """
11
+
12
+ import argparse
13
+ import os
14
+ import subprocess
15
+ import sys
16
+ from pathlib import Path
17
+
18
+
19
+ def get_package_dir() -> Path:
20
+ """Get the package directory containing requirements.txt."""
21
+ # When installed via npm, the script is symlinked in node_modules/.bin/
22
+ # We need to find the actual package directory
23
+ script_path = Path(__file__).resolve()
24
+
25
+ # Check if we're in a typical npm global install location
26
+ if "node_modules" in str(script_path):
27
+ # Navigate to the package root
28
+ parts = script_path.parts
29
+ for i, part in enumerate(parts):
30
+ if part == "node_modules":
31
+ # Find the package directory (usually @scope/package or just package)
32
+ remaining = parts[i + 1 :]
33
+ if remaining and remaining[0].startswith("@"):
34
+ # Scoped package: @rookiestar/eng-lang-tutor
35
+ pkg_dir = Path(*parts[: i + 3])
36
+ else:
37
+ # Unscoped package
38
+ pkg_dir = Path(*parts[: i + 2])
39
+ return pkg_dir
40
+
41
+ # Fallback: assume script is in scripts/ directory
42
+ return script_path.parent.parent
43
+
44
+
45
+ def check_dependencies() -> tuple[bool, list[str]]:
46
+ """Check if all required dependencies are installed."""
47
+ missing = []
48
+ required = ["websocket", "certifi", "aiohttp", "edge_tts"]
49
+
50
+ for module in required:
51
+ try:
52
+ __import__(module)
53
+ except ImportError:
54
+ missing.append(module)
55
+
56
+ return len(missing) == 0, missing
57
+
58
+
59
+ def install_dependencies(
60
+ requirements_path: Path, venv_path: Path | None = None, user: bool = False
61
+ ) -> bool:
62
+ """Install dependencies from requirements.txt."""
63
+ if not requirements_path.exists():
64
+ print(f"Error: requirements.txt not found at {requirements_path}")
65
+ return False
66
+
67
+ pip_cmd = [sys.executable, "-m", "pip", "install", "-r", str(requirements_path)]
68
+
69
+ if venv_path:
70
+ pip_cmd = [str(venv_path / "bin" / "pip"), "install", "-r", str(requirements_path)]
71
+ elif user:
72
+ pip_cmd.append("--user")
73
+
74
+ try:
75
+ print(f"Installing dependencies from {requirements_path}...")
76
+ subprocess.run(pip_cmd, check=True)
77
+ return True
78
+ except subprocess.CalledProcessError as e:
79
+ print(f"Error installing dependencies: {e}")
80
+ return False
81
+
82
+
83
+ def create_venv(venv_path: Path) -> bool:
84
+ """Create a virtual environment."""
85
+ try:
86
+ print(f"Creating virtual environment at {venv_path}...")
87
+ subprocess.run([sys.executable, "-m", "venv", str(venv_path)], check=True)
88
+ return True
89
+ except subprocess.CalledProcessError as e:
90
+ print(f"Error creating virtual environment: {e}")
91
+ return False
92
+
93
+
94
+ def main():
95
+ parser = argparse.ArgumentParser(
96
+ description="Setup script for eng-lang-tutor skill"
97
+ )
98
+ parser.add_argument(
99
+ "--venv",
100
+ metavar="PATH",
101
+ nargs="?",
102
+ const="~/.venvs/eng-lang-tutor",
103
+ help="Create a virtual environment and install dependencies (default: ~/.venvs/eng-lang-tutor)",
104
+ )
105
+ parser.add_argument(
106
+ "--check",
107
+ action="store_true",
108
+ help="Check if dependencies are installed without installing",
109
+ )
110
+ parser.add_argument(
111
+ "--user",
112
+ action="store_true",
113
+ help="Install dependencies to user directory (use with --break-system-packages if needed)",
114
+ )
115
+
116
+ args = parser.parse_args()
117
+
118
+ pkg_dir = get_package_dir()
119
+ requirements_path = pkg_dir / "requirements.txt"
120
+
121
+ # Check mode
122
+ if args.check:
123
+ ok, missing = check_dependencies()
124
+ if ok:
125
+ print("All dependencies are installed.")
126
+ sys.exit(0)
127
+ else:
128
+ print(f"Missing dependencies: {', '.join(missing)}")
129
+ print("Run this script without --check to install them.")
130
+ sys.exit(1)
131
+
132
+ # Venv mode
133
+ if args.venv:
134
+ venv_path = Path(args.venv).expanduser().resolve()
135
+
136
+ if not (venv_path / "bin" / "python").exists():
137
+ if not create_venv(venv_path):
138
+ sys.exit(1)
139
+
140
+ if install_dependencies(requirements_path, venv_path=venv_path):
141
+ print(f"\nSuccess! Virtual environment created at {venv_path}")
142
+ print(f"To activate it, run: source {venv_path}/bin/activate")
143
+ sys.exit(0)
144
+ else:
145
+ sys.exit(1)
146
+
147
+ # Direct install mode
148
+ if install_dependencies(requirements_path, user=args.user):
149
+ print("\nDependencies installed successfully!")
150
+ sys.exit(0)
151
+ else:
152
+ print("\nInstallation failed. Try one of these alternatives:")
153
+ print(" 1. Create a virtual environment: eng-lang-tutor-setup --venv")
154
+ print(" 2. Install to user directory: eng-lang-tutor-setup --user")
155
+ print(" 3. If you understand the risks: pip install -r requirements.txt --break-system-packages")
156
+ sys.exit(1)
157
+
158
+
159
+ if __name__ == "__main__":
160
+ main()