stock-weekly-report 0.1.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.
- package/README.md +245 -0
- package/bin/swr-mcp.js +9 -0
- package/bin/swr.js +9 -0
- package/cli.py +533 -0
- package/config.yaml.example +40 -0
- package/config_manager.py +29 -0
- package/fetch_episodes.py +216 -0
- package/mcp_server.py +158 -0
- package/package.json +33 -0
- package/pipeline.py +418 -0
- package/pyproject.toml +23 -0
- package/requirements.txt +13 -0
- package/run.sh +28 -0
- package/scripts/postinstall.js +70 -0
- package/send_report.py +511 -0
- package/transcribe.py +268 -0
- package/upload_to_notebooklm.py +270 -0
package/README.md
ADDED
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
# stock-weekly-report
|
|
2
|
+
|
|
3
|
+
By [Chang Yu Chuan](https://github.com/ChangYuChuan)
|
|
4
|
+
|
|
5
|
+
Automated pipeline that fetches Taiwanese stock podcast episodes, transcribes them with Whisper, uploads to NotebookLM, and emails a structured weekly investment report.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Installation
|
|
10
|
+
|
|
11
|
+
### Via npm (recommended)
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npm install -g stock-weekly-report
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
`postinstall` automatically finds Python 3.10+ on your system and creates the `venv14/` environment. After that, `swr` and `swr-mcp` are available globally.
|
|
18
|
+
|
|
19
|
+
Then set up the pipeline venv (needed for actual transcription runs):
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
cd $(npm root -g)/stock-weekly-report
|
|
23
|
+
python3 -m venv venv && venv/bin/pip install -r requirements.txt
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Finally run the setup wizard:
|
|
27
|
+
```bash
|
|
28
|
+
swr init
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
## Manual Setup
|
|
34
|
+
|
|
35
|
+
### 1. Prerequisites
|
|
36
|
+
|
|
37
|
+
- Python 3.9+ (pipeline) and Python 3.10+ (CLI/MCP — Homebrew `python3.14` recommended)
|
|
38
|
+
- [nlm](https://github.com/OpenClaw-AI/notebooklm-mcp-cli) binary (`~/.openclaw/workspace/venv/bin/nlm`)
|
|
39
|
+
- Gmail account with an [App Password](https://support.google.com/accounts/answer/185833)
|
|
40
|
+
|
|
41
|
+
### 2. Pipeline venv (Python 3.9)
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
python3 -m venv venv
|
|
45
|
+
venv/bin/pip install -r requirements.txt
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### 3. CLI + MCP venv (Python 3.10+)
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
/opt/homebrew/bin/python3.14 -m venv venv14
|
|
52
|
+
venv14/bin/pip install -e .
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
This installs two entry points:
|
|
56
|
+
- `venv14/bin/swr` — the CLI
|
|
57
|
+
- `venv14/bin/swr-mcp` — the MCP server
|
|
58
|
+
|
|
59
|
+
Optionally add `venv14/bin` to your `PATH`:
|
|
60
|
+
```bash
|
|
61
|
+
echo 'export PATH="/path/to/stock-weekly-report/venv14/bin:$PATH"' >> ~/.zprofile
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### 4. First-time configuration
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
swr init
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
The wizard prompts for data folder, nlm path, Gmail app password, sender/recipient emails, and retention periods. It writes `config.yaml` (git-ignored) and optionally installs the cron job.
|
|
71
|
+
|
|
72
|
+
To start from a template instead:
|
|
73
|
+
```bash
|
|
74
|
+
cp config.yaml.example config.yaml
|
|
75
|
+
# edit config.yaml, then export EMAIL_SMTP_PASSWORD in ~/.zprofile
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
## CLI reference
|
|
81
|
+
|
|
82
|
+
```
|
|
83
|
+
swr [--config PATH]
|
|
84
|
+
├── init Interactive setup wizard
|
|
85
|
+
├── run Run the pipeline
|
|
86
|
+
│ --skip-fetch Skip podcast download
|
|
87
|
+
│ --skip-transcribe Skip Whisper transcription
|
|
88
|
+
│ --skip-upload Skip NotebookLM upload
|
|
89
|
+
│ --skip-email Skip report generation entirely
|
|
90
|
+
│ --skip-cleanup Skip data cleanup
|
|
91
|
+
│ --save-report-only Generate report but don't send email
|
|
92
|
+
│ --folder FOLDER e.g. 20260218-20260225
|
|
93
|
+
│ --notebook-id ID Reuse an existing notebook (skips upload)
|
|
94
|
+
│
|
|
95
|
+
├── podcast
|
|
96
|
+
│ ├── list List configured feeds
|
|
97
|
+
│ ├── add NAME URL Add a feed
|
|
98
|
+
│ └── remove NAME Remove a feed
|
|
99
|
+
│
|
|
100
|
+
├── receiver
|
|
101
|
+
│ ├── list List configured recipients
|
|
102
|
+
│ ├── add EMAIL Add a recipient
|
|
103
|
+
│ └── remove EMAIL Remove a recipient
|
|
104
|
+
│
|
|
105
|
+
├── cron
|
|
106
|
+
│ ├── install [--schedule EXPR] Install (or update) the cron job
|
|
107
|
+
│ ├── remove Remove the cron job
|
|
108
|
+
│ └── status Show cron job status
|
|
109
|
+
│
|
|
110
|
+
├── config
|
|
111
|
+
│ ├── show Display current config (passwords redacted)
|
|
112
|
+
│ └── set KEY VALUE Set a value (e.g. retention.audio_months 6)
|
|
113
|
+
│
|
|
114
|
+
└── mcp Start the MCP server (stdio transport)
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### Common examples
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
# Full pipeline run
|
|
121
|
+
swr run
|
|
122
|
+
|
|
123
|
+
# Skip fetch + transcribe, reuse existing notebook, only send email
|
|
124
|
+
swr run --skip-fetch --skip-transcribe --notebook-id e08a19e4-b8b7-48dc-88d6-2f2393239c36
|
|
125
|
+
|
|
126
|
+
# Generate and save report locally without sending email
|
|
127
|
+
swr run --skip-fetch --skip-transcribe --notebook-id <id> --save-report-only
|
|
128
|
+
|
|
129
|
+
# Manage feeds
|
|
130
|
+
swr podcast list
|
|
131
|
+
swr podcast add "新節目" https://example.com/feed.xml
|
|
132
|
+
swr podcast remove "新節目"
|
|
133
|
+
|
|
134
|
+
# Manage recipients
|
|
135
|
+
swr receiver list
|
|
136
|
+
swr receiver add colleague@example.com
|
|
137
|
+
swr receiver remove colleague@example.com
|
|
138
|
+
|
|
139
|
+
# Cron job — install on Sundays at 8 AM
|
|
140
|
+
swr cron install
|
|
141
|
+
swr cron install --schedule "0 9 * * 0" # Sundays at 9 AM
|
|
142
|
+
swr cron status
|
|
143
|
+
swr cron remove
|
|
144
|
+
|
|
145
|
+
# Config
|
|
146
|
+
swr config show
|
|
147
|
+
swr config set retention.audio_months 6
|
|
148
|
+
swr config set retention.transcripts_months 3
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
---
|
|
152
|
+
|
|
153
|
+
## Retention
|
|
154
|
+
|
|
155
|
+
By default, audio files older than 3 months are deleted. Transcripts and reports are kept forever. Adjust in config:
|
|
156
|
+
|
|
157
|
+
```yaml
|
|
158
|
+
retention:
|
|
159
|
+
audio_months: 3 # 0 = keep forever
|
|
160
|
+
transcripts_months: 6 # 0 = keep forever
|
|
161
|
+
reports_months: 12 # 0 = keep forever
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
Or via CLI:
|
|
165
|
+
```bash
|
|
166
|
+
swr config set retention.audio_months 6
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
---
|
|
170
|
+
|
|
171
|
+
## MCP server
|
|
172
|
+
|
|
173
|
+
The MCP server exposes four tools for use with Claude Desktop or any MCP client:
|
|
174
|
+
|
|
175
|
+
| Tool | Description |
|
|
176
|
+
|---|---|
|
|
177
|
+
| `run_pipeline` | Run the pipeline (with stage selection, save-only mode) |
|
|
178
|
+
| `get_report` | Read a saved report (defaults to latest) |
|
|
179
|
+
| `list_reports` | List all available report folders |
|
|
180
|
+
| `get_logs` | Tail the pipeline log |
|
|
181
|
+
|
|
182
|
+
### Claude Desktop integration
|
|
183
|
+
|
|
184
|
+
If installed via npm:
|
|
185
|
+
```json
|
|
186
|
+
{
|
|
187
|
+
"mcpServers": {
|
|
188
|
+
"stock-weekly-report": {
|
|
189
|
+
"command": "swr-mcp"
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
If installed manually:
|
|
196
|
+
```json
|
|
197
|
+
{
|
|
198
|
+
"mcpServers": {
|
|
199
|
+
"stock-weekly-report": {
|
|
200
|
+
"command": "/Users/yuchuan/Projects/stock-weekly-report/venv14/bin/swr-mcp"
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
Start the server manually for testing:
|
|
207
|
+
```bash
|
|
208
|
+
swr mcp
|
|
209
|
+
# or
|
|
210
|
+
venv14/bin/swr-mcp
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
---
|
|
214
|
+
|
|
215
|
+
## Cron job
|
|
216
|
+
|
|
217
|
+
The cron job calls `run.sh`, which sources `~/.zprofile` (for `EMAIL_SMTP_PASSWORD`) and logs output to `logs/pipeline.log`.
|
|
218
|
+
|
|
219
|
+
```bash
|
|
220
|
+
swr cron install # Sundays at 8 AM (default)
|
|
221
|
+
swr cron install --schedule "0 9 * * 0" # Sundays at 9 AM
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
---
|
|
225
|
+
|
|
226
|
+
## Project structure
|
|
227
|
+
|
|
228
|
+
```
|
|
229
|
+
stock-weekly-report/
|
|
230
|
+
├── pipeline.py # Orchestrates all stages
|
|
231
|
+
├── fetch_episodes.py # Stage 1: download podcast episodes
|
|
232
|
+
├── transcribe.py # Stage 2: Whisper transcription
|
|
233
|
+
├── upload_to_notebooklm.py # Stage 3: NotebookLM upload
|
|
234
|
+
├── send_report.py # Stage 4: report generation + email
|
|
235
|
+
├── cli.py # swr CLI (click)
|
|
236
|
+
├── mcp_server.py # MCP server (FastMCP)
|
|
237
|
+
├── config_manager.py # Atomic config read/write helper
|
|
238
|
+
├── run.sh # Shell wrapper (sources ~/.zprofile, logs to file)
|
|
239
|
+
├── config.yaml # Your config (git-ignored, create with `swr init`)
|
|
240
|
+
├── config.yaml.example # Template
|
|
241
|
+
├── pyproject.toml # Entry point declarations
|
|
242
|
+
├── requirements.txt # Pipeline dependencies
|
|
243
|
+
├── venv/ # Python 3.9 venv (pipeline + transcription)
|
|
244
|
+
└── venv14/ # Python 3.14 venv (CLI + MCP server)
|
|
245
|
+
```
|
package/bin/swr-mcp.js
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
|
|
4
|
+
const { spawn } = require("child_process");
|
|
5
|
+
const path = require("path");
|
|
6
|
+
|
|
7
|
+
const bin = path.join(__dirname, "..", "venv14", "bin", "swr-mcp");
|
|
8
|
+
const proc = spawn(bin, process.argv.slice(2), { stdio: "inherit" });
|
|
9
|
+
proc.on("close", (code) => process.exit(code ?? 0));
|
package/bin/swr.js
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
|
|
4
|
+
const { spawn } = require("child_process");
|
|
5
|
+
const path = require("path");
|
|
6
|
+
|
|
7
|
+
const bin = path.join(__dirname, "..", "venv14", "bin", "swr");
|
|
8
|
+
const proc = spawn(bin, process.argv.slice(2), { stdio: "inherit" });
|
|
9
|
+
proc.on("close", (code) => process.exit(code ?? 0));
|