growth-tools 0.1.0__tar.gz
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.
- growth_tools-0.1.0/.env.example +32 -0
- growth_tools-0.1.0/.github/workflows/ci.yml +52 -0
- growth_tools-0.1.0/.gitignore +10 -0
- growth_tools-0.1.0/PKG-INFO +201 -0
- growth_tools-0.1.0/README.md +173 -0
- growth_tools-0.1.0/examples/sample-icp.env +39 -0
- growth_tools-0.1.0/examples/sample-leads.json +78 -0
- growth_tools-0.1.0/examples/test-output.txt +14 -0
- growth_tools-0.1.0/growth_tools/__init__.py +0 -0
- growth_tools-0.1.0/growth_tools/api/__init__.py +0 -0
- growth_tools-0.1.0/growth_tools/api/main.py +83 -0
- growth_tools-0.1.0/growth_tools/config/__init__.py +0 -0
- growth_tools-0.1.0/growth_tools/config/settings.py +68 -0
- growth_tools-0.1.0/growth_tools/core/__init__.py +0 -0
- growth_tools-0.1.0/growth_tools/core/db.py +153 -0
- growth_tools-0.1.0/growth_tools/core/llm.py +232 -0
- growth_tools-0.1.0/growth_tools/core/scoring.py +49 -0
- growth_tools-0.1.0/growth_tools/run_api.py +36 -0
- growth_tools-0.1.0/growth_tools/run_discord.py +20 -0
- growth_tools-0.1.0/growth_tools/run_reddit.py +33 -0
- growth_tools-0.1.0/growth_tools/systems/__init__.py +0 -0
- growth_tools-0.1.0/growth_tools/systems/crm_sequencer.py +151 -0
- growth_tools-0.1.0/growth_tools/systems/discord_bot.py +176 -0
- growth_tools-0.1.0/growth_tools/systems/github_auditor.py +179 -0
- growth_tools-0.1.0/growth_tools/systems/linkedin_bot.py +202 -0
- growth_tools-0.1.0/growth_tools/systems/linkedin_enricher.py +191 -0
- growth_tools-0.1.0/growth_tools/systems/reddit_capture.py +176 -0
- growth_tools-0.1.0/growth_tools/systems/website_auditor.py +121 -0
- growth_tools-0.1.0/pyproject.toml +48 -0
- growth_tools-0.1.0/requirements.txt +27 -0
- growth_tools-0.1.0/run_api.py +36 -0
- growth_tools-0.1.0/run_discord.py +20 -0
- growth_tools-0.1.0/run_reddit.py +33 -0
- growth_tools-0.1.0/src/growth_tools/__init__.py +0 -0
- growth_tools-0.1.0/src/growth_tools/api/__init__.py +0 -0
- growth_tools-0.1.0/src/growth_tools/api/main.py +84 -0
- growth_tools-0.1.0/src/growth_tools/config/__init__.py +0 -0
- growth_tools-0.1.0/src/growth_tools/config/settings.py +68 -0
- growth_tools-0.1.0/src/growth_tools/core/__init__.py +0 -0
- growth_tools-0.1.0/src/growth_tools/core/db.py +153 -0
- growth_tools-0.1.0/src/growth_tools/core/llm.py +232 -0
- growth_tools-0.1.0/src/growth_tools/core/scoring.py +49 -0
- growth_tools-0.1.0/src/growth_tools/systems/__init__.py +0 -0
- growth_tools-0.1.0/src/growth_tools/systems/crm_sequencer.py +151 -0
- growth_tools-0.1.0/src/growth_tools/systems/discord_bot.py +176 -0
- growth_tools-0.1.0/src/growth_tools/systems/github_auditor.py +179 -0
- growth_tools-0.1.0/src/growth_tools/systems/linkedin_bot.py +202 -0
- growth_tools-0.1.0/src/growth_tools/systems/linkedin_enricher.py +190 -0
- growth_tools-0.1.0/src/growth_tools/systems/reddit_capture.py +177 -0
- growth_tools-0.1.0/src/growth_tools/systems/website_auditor.py +120 -0
- growth_tools-0.1.0/tests/__init__.py +0 -0
- growth_tools-0.1.0/tests/test_brand_config.py +42 -0
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# ── Reddit ────────────────────────────────────────────────────────────────────
|
|
2
|
+
REDDIT_CLIENT_ID=your_reddit_app_id
|
|
3
|
+
REDDIT_CLIENT_SECRET=your_reddit_app_secret
|
|
4
|
+
REDDIT_USER_AGENT=growth-tools/1.0
|
|
5
|
+
|
|
6
|
+
# ── OpenAI (for classification + outreach drafts) ─────────────────────────────
|
|
7
|
+
OPENAI_API_KEY=sk-...
|
|
8
|
+
OPENAI_MODEL=gpt-4.1-mini
|
|
9
|
+
|
|
10
|
+
# ── Supabase (lead storage) ───────────────────────────────────────────────────
|
|
11
|
+
SUPABASE_URL=https://your-project.supabase.co
|
|
12
|
+
SUPABASE_SERVICE_ROLE_KEY=eyJ...
|
|
13
|
+
|
|
14
|
+
# ── Discord ───────────────────────────────────────────────────────────────────
|
|
15
|
+
DISCORD_TOKEN=your_discord_bot_token
|
|
16
|
+
# Comma-separated channel IDs to monitor (leave blank = all channels)
|
|
17
|
+
ALLOWED_CHANNEL_IDS=
|
|
18
|
+
|
|
19
|
+
# ── GitHub ────────────────────────────────────────────────────────────────────
|
|
20
|
+
GITHUB_TOKEN=ghp_...
|
|
21
|
+
|
|
22
|
+
# ── Scoring thresholds ────────────────────────────────────────────────────────
|
|
23
|
+
# Min intent score (0-100) to save a lead
|
|
24
|
+
LEAD_INTENT_THRESHOLD=70
|
|
25
|
+
# Min confidence for Discord auto-reply
|
|
26
|
+
DISCORD_CONFIDENCE_THRESHOLD=0.75
|
|
27
|
+
|
|
28
|
+
# ── Brand identity (used in LLM-generated reply/outreach drafts) ──────────────
|
|
29
|
+
BRAND_NAME=YourProduct
|
|
30
|
+
BRAND_TAGLINE=helps teams ship production-ready apps
|
|
31
|
+
# Describe the core problem your ICP (ideal customer profile) has
|
|
32
|
+
ICP_PAIN=move from prototype to production
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
test:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
strategy:
|
|
12
|
+
matrix:
|
|
13
|
+
python-version: ["3.10", "3.11", "3.12"]
|
|
14
|
+
|
|
15
|
+
steps:
|
|
16
|
+
- uses: actions/checkout@v4
|
|
17
|
+
|
|
18
|
+
- uses: actions/setup-python@v5
|
|
19
|
+
with:
|
|
20
|
+
python-version: ${{ matrix.python-version }}
|
|
21
|
+
|
|
22
|
+
- name: Install package
|
|
23
|
+
run: pip install -e ".[dev]"
|
|
24
|
+
|
|
25
|
+
- name: Run tests
|
|
26
|
+
run: pytest tests/ -v
|
|
27
|
+
|
|
28
|
+
- name: Lint
|
|
29
|
+
run: |
|
|
30
|
+
pip install ruff
|
|
31
|
+
ruff check src/
|
|
32
|
+
|
|
33
|
+
dry-run-reddit:
|
|
34
|
+
runs-on: ubuntu-latest
|
|
35
|
+
steps:
|
|
36
|
+
- uses: actions/checkout@v4
|
|
37
|
+
- uses: actions/setup-python@v5
|
|
38
|
+
with:
|
|
39
|
+
python-version: "3.11"
|
|
40
|
+
- name: Install package
|
|
41
|
+
run: pip install -e .
|
|
42
|
+
- name: Smoke test CLI (dry-run)
|
|
43
|
+
run: growth-reddit --dry-run --subreddits SaaS
|
|
44
|
+
env:
|
|
45
|
+
BRAND_NAME: TestBrand
|
|
46
|
+
BRAND_TAGLINE: Test tagline
|
|
47
|
+
ICP_PAIN: Test pain
|
|
48
|
+
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
|
|
49
|
+
REDDIT_CLIENT_ID: ${{ secrets.REDDIT_CLIENT_ID }}
|
|
50
|
+
REDDIT_CLIENT_SECRET: ${{ secrets.REDDIT_CLIENT_SECRET }}
|
|
51
|
+
REDDIT_USER_AGENT: CI/1.0
|
|
52
|
+
continue-on-error: true
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: growth-tools
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Lead capture from Reddit, Discord and GitHub with hybrid LLM scoring and outreach drafts.
|
|
5
|
+
Project-URL: Repository, https://github.com/nometria/growth-tools
|
|
6
|
+
License: MIT
|
|
7
|
+
Keywords: discord,growth,leads,outreach,reddit,saas,scoring
|
|
8
|
+
Classifier: Development Status :: 4 - Beta
|
|
9
|
+
Classifier: Intended Audience :: Developers
|
|
10
|
+
Classifier: Topic :: Internet :: WWW/HTTP
|
|
11
|
+
Requires-Python: >=3.10
|
|
12
|
+
Requires-Dist: beautifulsoup4>=4.12.0
|
|
13
|
+
Requires-Dist: discord-py>=2.3.0
|
|
14
|
+
Requires-Dist: fastapi>=0.115.0
|
|
15
|
+
Requires-Dist: openai>=1.52.0
|
|
16
|
+
Requires-Dist: praw>=7.7.0
|
|
17
|
+
Requires-Dist: pydantic-settings>=2.0.0
|
|
18
|
+
Requires-Dist: pydantic>=2.0.0
|
|
19
|
+
Requires-Dist: python-dotenv>=1.0.0
|
|
20
|
+
Requires-Dist: requests>=2.31.0
|
|
21
|
+
Requires-Dist: supabase>=2.0.0
|
|
22
|
+
Requires-Dist: tenacity>=8.2.0
|
|
23
|
+
Requires-Dist: uvicorn[standard]>=0.30.0
|
|
24
|
+
Provides-Extra: dev
|
|
25
|
+
Requires-Dist: pytest-cov; extra == 'dev'
|
|
26
|
+
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
27
|
+
Description-Content-Type: text/markdown
|
|
28
|
+
|
|
29
|
+
# growth-tools
|
|
30
|
+
|
|
31
|
+
> Automated lead capture from Reddit, Discord and GitHub — with hybrid LLM scoring and outreach drafts.
|
|
32
|
+
|
|
33
|
+
Built for dev-tool and SaaS companies that want inbound signal from developer communities
|
|
34
|
+
without hiring a full-time growth team.
|
|
35
|
+
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
## Quick start
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
# Clone and install
|
|
42
|
+
git clone https://github.com/ownmy-app/growth-tools
|
|
43
|
+
cd growth-tools
|
|
44
|
+
pip install -e .
|
|
45
|
+
|
|
46
|
+
# Configure your environment
|
|
47
|
+
cp examples/sample-icp.env .env
|
|
48
|
+
# Edit .env with your API keys and brand config
|
|
49
|
+
|
|
50
|
+
# Run Reddit monitor (one-shot)
|
|
51
|
+
growth-reddit
|
|
52
|
+
|
|
53
|
+
# Run the website auditor API
|
|
54
|
+
growth-api
|
|
55
|
+
# or: uvicorn growth_tools.api.main:app --port 8000
|
|
56
|
+
|
|
57
|
+
# Run tests
|
|
58
|
+
pytest tests/ -v
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Required environment variables (see `examples/sample-icp.env`):
|
|
62
|
+
```bash
|
|
63
|
+
OPENAI_API_KEY=sk-proj-...
|
|
64
|
+
REDDIT_CLIENT_ID=...
|
|
65
|
+
REDDIT_CLIENT_SECRET=...
|
|
66
|
+
BRAND_NAME="Your Product"
|
|
67
|
+
ICP_KEYWORDS="supabase,self-host,postgres,..."
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
---
|
|
71
|
+
|
|
72
|
+
## What's included
|
|
73
|
+
|
|
74
|
+
| Module | What it does |
|
|
75
|
+
|--------|-------------|
|
|
76
|
+
| `systems/reddit_capture.py` | Monitors subreddits, two-stage filter (keyword → LLM), saves hot leads |
|
|
77
|
+
| `systems/discord_bot.py` | Discord bot with per-channel cooldown, confidence threshold gating |
|
|
78
|
+
| `systems/website_auditor.py` | Detects tech stack from HTML/headers (Next.js, Vite, Supabase, Vercel…) |
|
|
79
|
+
| `systems/github_auditor.py` | Scans repos for migration readiness (package.json, Dockerfile analysis) |
|
|
80
|
+
| `systems/crm_sequencer.py` | LLM-generated outreach drafts (capped at 90 words for reply rates) |
|
|
81
|
+
| `core/scoring.py` | Hybrid rule + LLM scoring: `0.5 × rule_score + 0.5 × llm_intent_score` |
|
|
82
|
+
| `core/llm.py` | OpenAI client with fallback model + tenacity retries |
|
|
83
|
+
| `api/main.py` | FastAPI: `POST /audit/website`, `POST /audit/github`, `GET /health` |
|
|
84
|
+
|
|
85
|
+
---
|
|
86
|
+
|
|
87
|
+
## Scoring tiers
|
|
88
|
+
|
|
89
|
+
| Score | Tier | Action |
|
|
90
|
+
|-------|------|--------|
|
|
91
|
+
| ≥ 80 | **hot** | Immediate outreach |
|
|
92
|
+
| 60–79 | **nurture** | Add to sequence |
|
|
93
|
+
| 40–59 | **educate** | Send content |
|
|
94
|
+
| < 40 | **ignore** | Skip |
|
|
95
|
+
|
|
96
|
+
Rule signals: `+25` high-intent builder (Lovable/Replit/Bolt/v0), `+30` high-intent pain
|
|
97
|
+
(deploy/migrate/security/ownership), `+20` has public repo, `+25` mentions clients.
|
|
98
|
+
Blended 50/50 with LLM intent score.
|
|
99
|
+
|
|
100
|
+
---
|
|
101
|
+
|
|
102
|
+
## Supabase schema
|
|
103
|
+
|
|
104
|
+
```sql
|
|
105
|
+
create table lead_signals (
|
|
106
|
+
id uuid primary key default gen_random_uuid(),
|
|
107
|
+
source text, -- 'reddit' | 'discord' | 'github'
|
|
108
|
+
title text,
|
|
109
|
+
body text,
|
|
110
|
+
url text,
|
|
111
|
+
author text,
|
|
112
|
+
intent_score int,
|
|
113
|
+
tier text,
|
|
114
|
+
builder text,
|
|
115
|
+
pain_type text,
|
|
116
|
+
reply_draft text,
|
|
117
|
+
created_at timestamptz default now()
|
|
118
|
+
);
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
---
|
|
122
|
+
|
|
123
|
+
## Setup
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
git clone https://github.com/ownmy-app/growth-tools
|
|
127
|
+
cd growth-tools
|
|
128
|
+
pip install -e .
|
|
129
|
+
cp examples/sample-icp.env .env
|
|
130
|
+
# Edit .env
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
---
|
|
134
|
+
|
|
135
|
+
## Run
|
|
136
|
+
|
|
137
|
+
```bash
|
|
138
|
+
# Reddit monitor (one-shot)
|
|
139
|
+
growth-reddit
|
|
140
|
+
|
|
141
|
+
# Discord bot (persistent)
|
|
142
|
+
python -m growth_tools.systems.discord_bot
|
|
143
|
+
|
|
144
|
+
# Website auditor API
|
|
145
|
+
growth-api
|
|
146
|
+
# or: uvicorn growth_tools.api.main:app --port 8000
|
|
147
|
+
|
|
148
|
+
# Audit a specific website
|
|
149
|
+
curl -X POST http://localhost:8000/audit/website -H 'Content-Type: application/json' \
|
|
150
|
+
-d '{"url": "https://example.com"}'
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
---
|
|
154
|
+
|
|
155
|
+
## Customise
|
|
156
|
+
|
|
157
|
+
**Target subreddits** — set `ICP_KEYWORDS` in `.env`
|
|
158
|
+
|
|
159
|
+
**Lead scoring** — edit weights in `core/scoring.py` (or move to YAML config)
|
|
160
|
+
|
|
161
|
+
**Outreach tone** — edit prompts in `core/llm.py`
|
|
162
|
+
|
|
163
|
+
---
|
|
164
|
+
|
|
165
|
+
## Immediate next steps
|
|
166
|
+
1. Make subreddits + keywords configurable via env / YAML
|
|
167
|
+
2. Add GitHub lead capture (scan repos that import competitor SDKs)
|
|
168
|
+
3. Add Slack notification on "hot" leads
|
|
169
|
+
4. Package as `pip install growth-tools`
|
|
170
|
+
|
|
171
|
+
---
|
|
172
|
+
|
|
173
|
+
## Commercial viability
|
|
174
|
+
- Open-core: open source the capture + scoring, charge for the CRM sequencer
|
|
175
|
+
- SaaS: $200–500/mo per team for managed lead pipeline
|
|
176
|
+
- Competitors: Trigify, Drippi — neither does GitHub + Reddit + LLM scoring combined
|
|
177
|
+
|
|
178
|
+
---
|
|
179
|
+
|
|
180
|
+
## Example output
|
|
181
|
+
|
|
182
|
+
Running `pytest tests/ -v`:
|
|
183
|
+
|
|
184
|
+
```
|
|
185
|
+
============================= test session starts ==============================
|
|
186
|
+
platform darwin -- Python 3.13.9, pytest-9.0.2, pluggy-1.5.0
|
|
187
|
+
cachedir: .pytest_cache
|
|
188
|
+
rootdir: /tmp/ownmy-releases/growth-tools
|
|
189
|
+
configfile: pyproject.toml
|
|
190
|
+
plugins: anyio-4.12.1, cov-7.1.0
|
|
191
|
+
collecting ... collected 4 items
|
|
192
|
+
|
|
193
|
+
tests/test_brand_config.py::test_brand_name_reads_from_env PASSED [ 25%]
|
|
194
|
+
tests/test_brand_config.py::test_brand_tagline_reads_from_env PASSED [ 50%]
|
|
195
|
+
tests/test_brand_config.py::test_icp_pain_reads_from_env PASSED [ 75%]
|
|
196
|
+
tests/test_brand_config.py::test_no_hardcoded_brand_names_in_source PASSED [100%]
|
|
197
|
+
|
|
198
|
+
============================== 4 passed in 0.03s ===============================
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
See `examples/sample-leads.json` for representative scored lead output and `examples/sample-icp.env` for required environment variable configuration.
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
# growth-tools
|
|
2
|
+
|
|
3
|
+
> Automated lead capture from Reddit, Discord and GitHub — with hybrid LLM scoring and outreach drafts.
|
|
4
|
+
|
|
5
|
+
Built for dev-tool and SaaS companies that want inbound signal from developer communities
|
|
6
|
+
without hiring a full-time growth team.
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## Quick start
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
# Clone and install
|
|
14
|
+
git clone https://github.com/ownmy-app/growth-tools
|
|
15
|
+
cd growth-tools
|
|
16
|
+
pip install -e .
|
|
17
|
+
|
|
18
|
+
# Configure your environment
|
|
19
|
+
cp examples/sample-icp.env .env
|
|
20
|
+
# Edit .env with your API keys and brand config
|
|
21
|
+
|
|
22
|
+
# Run Reddit monitor (one-shot)
|
|
23
|
+
growth-reddit
|
|
24
|
+
|
|
25
|
+
# Run the website auditor API
|
|
26
|
+
growth-api
|
|
27
|
+
# or: uvicorn growth_tools.api.main:app --port 8000
|
|
28
|
+
|
|
29
|
+
# Run tests
|
|
30
|
+
pytest tests/ -v
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Required environment variables (see `examples/sample-icp.env`):
|
|
34
|
+
```bash
|
|
35
|
+
OPENAI_API_KEY=sk-proj-...
|
|
36
|
+
REDDIT_CLIENT_ID=...
|
|
37
|
+
REDDIT_CLIENT_SECRET=...
|
|
38
|
+
BRAND_NAME="Your Product"
|
|
39
|
+
ICP_KEYWORDS="supabase,self-host,postgres,..."
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
## What's included
|
|
45
|
+
|
|
46
|
+
| Module | What it does |
|
|
47
|
+
|--------|-------------|
|
|
48
|
+
| `systems/reddit_capture.py` | Monitors subreddits, two-stage filter (keyword → LLM), saves hot leads |
|
|
49
|
+
| `systems/discord_bot.py` | Discord bot with per-channel cooldown, confidence threshold gating |
|
|
50
|
+
| `systems/website_auditor.py` | Detects tech stack from HTML/headers (Next.js, Vite, Supabase, Vercel…) |
|
|
51
|
+
| `systems/github_auditor.py` | Scans repos for migration readiness (package.json, Dockerfile analysis) |
|
|
52
|
+
| `systems/crm_sequencer.py` | LLM-generated outreach drafts (capped at 90 words for reply rates) |
|
|
53
|
+
| `core/scoring.py` | Hybrid rule + LLM scoring: `0.5 × rule_score + 0.5 × llm_intent_score` |
|
|
54
|
+
| `core/llm.py` | OpenAI client with fallback model + tenacity retries |
|
|
55
|
+
| `api/main.py` | FastAPI: `POST /audit/website`, `POST /audit/github`, `GET /health` |
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
## Scoring tiers
|
|
60
|
+
|
|
61
|
+
| Score | Tier | Action |
|
|
62
|
+
|-------|------|--------|
|
|
63
|
+
| ≥ 80 | **hot** | Immediate outreach |
|
|
64
|
+
| 60–79 | **nurture** | Add to sequence |
|
|
65
|
+
| 40–59 | **educate** | Send content |
|
|
66
|
+
| < 40 | **ignore** | Skip |
|
|
67
|
+
|
|
68
|
+
Rule signals: `+25` high-intent builder (Lovable/Replit/Bolt/v0), `+30` high-intent pain
|
|
69
|
+
(deploy/migrate/security/ownership), `+20` has public repo, `+25` mentions clients.
|
|
70
|
+
Blended 50/50 with LLM intent score.
|
|
71
|
+
|
|
72
|
+
---
|
|
73
|
+
|
|
74
|
+
## Supabase schema
|
|
75
|
+
|
|
76
|
+
```sql
|
|
77
|
+
create table lead_signals (
|
|
78
|
+
id uuid primary key default gen_random_uuid(),
|
|
79
|
+
source text, -- 'reddit' | 'discord' | 'github'
|
|
80
|
+
title text,
|
|
81
|
+
body text,
|
|
82
|
+
url text,
|
|
83
|
+
author text,
|
|
84
|
+
intent_score int,
|
|
85
|
+
tier text,
|
|
86
|
+
builder text,
|
|
87
|
+
pain_type text,
|
|
88
|
+
reply_draft text,
|
|
89
|
+
created_at timestamptz default now()
|
|
90
|
+
);
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
---
|
|
94
|
+
|
|
95
|
+
## Setup
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
git clone https://github.com/ownmy-app/growth-tools
|
|
99
|
+
cd growth-tools
|
|
100
|
+
pip install -e .
|
|
101
|
+
cp examples/sample-icp.env .env
|
|
102
|
+
# Edit .env
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
---
|
|
106
|
+
|
|
107
|
+
## Run
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
# Reddit monitor (one-shot)
|
|
111
|
+
growth-reddit
|
|
112
|
+
|
|
113
|
+
# Discord bot (persistent)
|
|
114
|
+
python -m growth_tools.systems.discord_bot
|
|
115
|
+
|
|
116
|
+
# Website auditor API
|
|
117
|
+
growth-api
|
|
118
|
+
# or: uvicorn growth_tools.api.main:app --port 8000
|
|
119
|
+
|
|
120
|
+
# Audit a specific website
|
|
121
|
+
curl -X POST http://localhost:8000/audit/website -H 'Content-Type: application/json' \
|
|
122
|
+
-d '{"url": "https://example.com"}'
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
---
|
|
126
|
+
|
|
127
|
+
## Customise
|
|
128
|
+
|
|
129
|
+
**Target subreddits** — set `ICP_KEYWORDS` in `.env`
|
|
130
|
+
|
|
131
|
+
**Lead scoring** — edit weights in `core/scoring.py` (or move to YAML config)
|
|
132
|
+
|
|
133
|
+
**Outreach tone** — edit prompts in `core/llm.py`
|
|
134
|
+
|
|
135
|
+
---
|
|
136
|
+
|
|
137
|
+
## Immediate next steps
|
|
138
|
+
1. Make subreddits + keywords configurable via env / YAML
|
|
139
|
+
2. Add GitHub lead capture (scan repos that import competitor SDKs)
|
|
140
|
+
3. Add Slack notification on "hot" leads
|
|
141
|
+
4. Package as `pip install growth-tools`
|
|
142
|
+
|
|
143
|
+
---
|
|
144
|
+
|
|
145
|
+
## Commercial viability
|
|
146
|
+
- Open-core: open source the capture + scoring, charge for the CRM sequencer
|
|
147
|
+
- SaaS: $200–500/mo per team for managed lead pipeline
|
|
148
|
+
- Competitors: Trigify, Drippi — neither does GitHub + Reddit + LLM scoring combined
|
|
149
|
+
|
|
150
|
+
---
|
|
151
|
+
|
|
152
|
+
## Example output
|
|
153
|
+
|
|
154
|
+
Running `pytest tests/ -v`:
|
|
155
|
+
|
|
156
|
+
```
|
|
157
|
+
============================= test session starts ==============================
|
|
158
|
+
platform darwin -- Python 3.13.9, pytest-9.0.2, pluggy-1.5.0
|
|
159
|
+
cachedir: .pytest_cache
|
|
160
|
+
rootdir: /tmp/ownmy-releases/growth-tools
|
|
161
|
+
configfile: pyproject.toml
|
|
162
|
+
plugins: anyio-4.12.1, cov-7.1.0
|
|
163
|
+
collecting ... collected 4 items
|
|
164
|
+
|
|
165
|
+
tests/test_brand_config.py::test_brand_name_reads_from_env PASSED [ 25%]
|
|
166
|
+
tests/test_brand_config.py::test_brand_tagline_reads_from_env PASSED [ 50%]
|
|
167
|
+
tests/test_brand_config.py::test_icp_pain_reads_from_env PASSED [ 75%]
|
|
168
|
+
tests/test_brand_config.py::test_no_hardcoded_brand_names_in_source PASSED [100%]
|
|
169
|
+
|
|
170
|
+
============================== 4 passed in 0.03s ===============================
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
See `examples/sample-leads.json` for representative scored lead output and `examples/sample-icp.env` for required environment variable configuration.
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# Growth Tools — Required Environment Variables
|
|
2
|
+
# Copy this to .env and fill in your values
|
|
3
|
+
|
|
4
|
+
# ── Brand / ICP Configuration ─────────────────────────────────────────────────
|
|
5
|
+
# These define YOUR product so the LLM generates accurate outreach
|
|
6
|
+
BRAND_NAME="Acme DevTools"
|
|
7
|
+
BRAND_TAGLINE="The fastest way to self-host your AI-generated app"
|
|
8
|
+
ICP_PAIN="teams that built with Lovable, Bolt, or Manus and now need to self-host"
|
|
9
|
+
ICP_KEYWORDS="supabase,self-host,postgres,multi-tenant,AI-generated,Lovable,Bolt,Manus,v0"
|
|
10
|
+
OUTREACH_CTA="Want a 5-minute demo?"
|
|
11
|
+
|
|
12
|
+
# ── Reddit Credentials ────────────────────────────────────────────────────────
|
|
13
|
+
REDDIT_CLIENT_ID=your_reddit_client_id
|
|
14
|
+
REDDIT_CLIENT_SECRET=your_reddit_client_secret
|
|
15
|
+
REDDIT_USER_AGENT=growth-tools/0.1 by u/your_reddit_username
|
|
16
|
+
REDDIT_USERNAME=your_reddit_username
|
|
17
|
+
REDDIT_PASSWORD=your_reddit_password
|
|
18
|
+
|
|
19
|
+
# Subreddits to monitor (comma-separated)
|
|
20
|
+
REDDIT_SUBREDDITS=SaaS,webdev,nextjs,reactjs,Supabase,startups,entrepreneur
|
|
21
|
+
|
|
22
|
+
# ── Discord Configuration ─────────────────────────────────────────────────────
|
|
23
|
+
DISCORD_BOT_TOKEN=your_discord_bot_token
|
|
24
|
+
DISCORD_GUILD_IDS=123456789012345678,987654321098765432
|
|
25
|
+
|
|
26
|
+
# ── OpenAI (for LLM scoring) ──────────────────────────────────────────────────
|
|
27
|
+
OPENAI_API_KEY=sk-proj-...
|
|
28
|
+
OPENAI_MODEL=gpt-4o-mini
|
|
29
|
+
LLM_SCORE_THRESHOLD=0.6
|
|
30
|
+
|
|
31
|
+
# ── Supabase (for lead storage) ───────────────────────────────────────────────
|
|
32
|
+
SUPABASE_URL=https://your-project-ref.supabase.co
|
|
33
|
+
SUPABASE_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
|
|
34
|
+
|
|
35
|
+
# ── Scoring Weights ───────────────────────────────────────────────────────────
|
|
36
|
+
# Final score = RULE_WEIGHT * rule_score + LLM_WEIGHT * llm_score
|
|
37
|
+
RULE_WEIGHT=0.5
|
|
38
|
+
LLM_WEIGHT=0.5
|
|
39
|
+
MIN_SCORE_THRESHOLD=0.55
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"id": "reddit_abc123",
|
|
4
|
+
"source": "reddit",
|
|
5
|
+
"platform_url": "https://reddit.com/r/SaaS/comments/abc123/is_there_a_good_solution_for_multi_tenant_auth",
|
|
6
|
+
"author": "u/buildingmysaas",
|
|
7
|
+
"content": "We're trying to build a multi-tenant SaaS on top of Supabase but running into issues with RLS and per-tenant schemas. Anyone have a good solution? Currently we're on Vercel + Next.js.",
|
|
8
|
+
"subreddit": "SaaS",
|
|
9
|
+
"score": 0.87,
|
|
10
|
+
"rule_score": 0.75,
|
|
11
|
+
"llm_intent_score": 0.99,
|
|
12
|
+
"scoring_breakdown": {
|
|
13
|
+
"keyword_hits": ["supabase", "multi-tenant", "RLS", "Next.js"],
|
|
14
|
+
"pain_match": "schema isolation, auth complexity",
|
|
15
|
+
"intent": "actively evaluating solutions"
|
|
16
|
+
},
|
|
17
|
+
"outreach_draft": "Hey! Saw your post about multi-tenant Supabase — we built a schema-per-tenant migration tool that handles exactly this. It sets up isolated app_{id} schemas with RLS and Supabase auth wired in. Happy to share a quick demo if that's useful.",
|
|
18
|
+
"captured_at": "2026-03-22T09:14:32Z",
|
|
19
|
+
"status": "new"
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
"id": "reddit_def456",
|
|
23
|
+
"source": "reddit",
|
|
24
|
+
"platform_url": "https://reddit.com/r/webdev/comments/def456/migrating_from_firebase_to_supabase",
|
|
25
|
+
"author": "u/migrationpain",
|
|
26
|
+
"content": "Has anyone migrated from Firebase to Supabase? We have ~50k users and need to move auth and the Firestore data. Looking for tools that can help automate this.",
|
|
27
|
+
"subreddit": "webdev",
|
|
28
|
+
"score": 0.71,
|
|
29
|
+
"rule_score": 0.65,
|
|
30
|
+
"llm_intent_score": 0.77,
|
|
31
|
+
"scoring_breakdown": {
|
|
32
|
+
"keyword_hits": ["firebase", "supabase", "migrate", "auth"],
|
|
33
|
+
"pain_match": "migration complexity, user data",
|
|
34
|
+
"intent": "looking for migration tooling"
|
|
35
|
+
},
|
|
36
|
+
"outreach_draft": "We help teams migrate from Firebase to Supabase — auth users, Firestore collections, and storage. Done it for teams with 100k+ users. Worth a call?",
|
|
37
|
+
"captured_at": "2026-03-22T10:02:11Z",
|
|
38
|
+
"status": "new"
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
"id": "discord_ghi789",
|
|
42
|
+
"source": "discord",
|
|
43
|
+
"platform_url": "https://discord.com/channels/1016518.../#general",
|
|
44
|
+
"author": "devuser#4821",
|
|
45
|
+
"content": "anyone tried deploying a manus.im project to their own infra? want to use postgres instead of their managed db",
|
|
46
|
+
"channel": "general",
|
|
47
|
+
"score": 0.63,
|
|
48
|
+
"rule_score": 0.58,
|
|
49
|
+
"llm_intent_score": 0.68,
|
|
50
|
+
"scoring_breakdown": {
|
|
51
|
+
"keyword_hits": ["manus", "postgres", "deploy", "infra"],
|
|
52
|
+
"pain_match": "vendor lock-in, self-hosting",
|
|
53
|
+
"intent": "exploring self-hosting options"
|
|
54
|
+
},
|
|
55
|
+
"outreach_draft": "Hey! We have a one-command tool that migrates Manus projects to self-hosted Supabase + Postgres. Takes about 5 minutes. DM me if you want to try it.",
|
|
56
|
+
"captured_at": "2026-03-22T11:45:00Z",
|
|
57
|
+
"status": "contacted"
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
"id": "reddit_jkl012",
|
|
61
|
+
"source": "reddit",
|
|
62
|
+
"platform_url": "https://reddit.com/r/nextjs/comments/jkl012/security_review_of_ai_generated_code",
|
|
63
|
+
"author": "u/securityminded",
|
|
64
|
+
"content": "We used Bolt to generate our MVP and now our CTO wants a security review before we go to production. Anyone know a good tool that catches secrets and SQL injection in AI-generated code?",
|
|
65
|
+
"subreddit": "nextjs",
|
|
66
|
+
"score": 0.92,
|
|
67
|
+
"rule_score": 0.88,
|
|
68
|
+
"llm_intent_score": 0.96,
|
|
69
|
+
"scoring_breakdown": {
|
|
70
|
+
"keyword_hits": ["bolt", "security", "secrets", "SQL injection", "AI-generated"],
|
|
71
|
+
"pain_match": "production security, AI code quality",
|
|
72
|
+
"intent": "actively searching for a specific tool"
|
|
73
|
+
},
|
|
74
|
+
"outreach_draft": "We built a free static scanner specifically for AI-generated code (Bolt, Lovable, v0). Catches hardcoded secrets, SQL injection, CORS misconfigs — zero dependencies, runs in CI. Check it out at github.com/ownmy-app/security-scanner",
|
|
75
|
+
"captured_at": "2026-03-22T14:30:55Z",
|
|
76
|
+
"status": "new"
|
|
77
|
+
}
|
|
78
|
+
]
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
============================= test session starts ==============================
|
|
2
|
+
platform darwin -- Python 3.13.9, pytest-9.0.2, pluggy-1.5.0 -- /Users/architsharma/miniconda3/bin/python3.13
|
|
3
|
+
cachedir: .pytest_cache
|
|
4
|
+
rootdir: /private/tmp/ownmy-releases/growth-tools
|
|
5
|
+
configfile: pyproject.toml
|
|
6
|
+
plugins: anyio-4.12.1, cov-7.1.0
|
|
7
|
+
collecting ... collected 4 items
|
|
8
|
+
|
|
9
|
+
tests/test_brand_config.py::test_brand_name_reads_from_env PASSED [ 25%]
|
|
10
|
+
tests/test_brand_config.py::test_brand_tagline_reads_from_env PASSED [ 50%]
|
|
11
|
+
tests/test_brand_config.py::test_icp_pain_reads_from_env PASSED [ 75%]
|
|
12
|
+
tests/test_brand_config.py::test_no_hardcoded_brand_names_in_source PASSED [100%]
|
|
13
|
+
|
|
14
|
+
============================== 4 passed in 0.03s ===============================
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
"""
|
|
2
|
+
FastAPI app: website auditor + GitHub repo auditor.
|
|
3
|
+
Run: uvicorn api.main:app --reload (from growth-tools dir)
|
|
4
|
+
"""
|
|
5
|
+
import sys
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
_root = Path(__file__).resolve().parent.parent
|
|
9
|
+
if str(_root) not in sys.path:
|
|
10
|
+
sys.path.insert(0, str(_root))
|
|
11
|
+
|
|
12
|
+
from fastapi import FastAPI, HTTPException
|
|
13
|
+
from fastapi.middleware.cors import CORSMiddleware
|
|
14
|
+
from pydantic import BaseModel
|
|
15
|
+
|
|
16
|
+
from systems.website_auditor import audit_url as website_audit
|
|
17
|
+
from systems.github_auditor import analyze_repo_url as github_audit
|
|
18
|
+
from systems.crm_sequencer import add_lead
|
|
19
|
+
from core.db import is_db_available
|
|
20
|
+
|
|
21
|
+
app = FastAPI(
|
|
22
|
+
title="Growth Tools API",
|
|
23
|
+
description="Website auditor & GitHub repo auditor for production-readiness.",
|
|
24
|
+
version="1.0.0",
|
|
25
|
+
)
|
|
26
|
+
app.add_middleware(
|
|
27
|
+
CORSMiddleware,
|
|
28
|
+
allow_origins=["*"],
|
|
29
|
+
allow_credentials=True,
|
|
30
|
+
allow_methods=["*"],
|
|
31
|
+
allow_headers=["*"],
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class AuditWebsiteRequest(BaseModel):
|
|
36
|
+
url: str
|
|
37
|
+
save_as_lead: bool = False
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class AuditGitHubRequest(BaseModel):
|
|
41
|
+
repo_url: str
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
@app.get("/health")
|
|
45
|
+
def health():
|
|
46
|
+
return {"status": "ok", "db": is_db_available()}
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
@app.post("/audit/website")
|
|
50
|
+
def audit_website(req: AuditWebsiteRequest):
|
|
51
|
+
"""Paste app URL; returns detected stack, risks, and next step. Optionally saves as lead."""
|
|
52
|
+
if not req.url or len(req.url) > 2048:
|
|
53
|
+
raise HTTPException(status_code=400, detail="Invalid URL")
|
|
54
|
+
result = website_audit(req.url)
|
|
55
|
+
if req.save_as_lead and result.get("ok") and is_db_available():
|
|
56
|
+
try:
|
|
57
|
+
lead = add_lead(
|
|
58
|
+
source=result.get("url", req.url),
|
|
59
|
+
source_url=result.get("url", req.url),
|
|
60
|
+
title=result.get("title"),
|
|
61
|
+
platform="website",
|
|
62
|
+
pain_type="audit",
|
|
63
|
+
intent_score=50,
|
|
64
|
+
metadata={"audit": result},
|
|
65
|
+
)
|
|
66
|
+
if lead:
|
|
67
|
+
result["lead_id"] = lead.get("id")
|
|
68
|
+
except Exception:
|
|
69
|
+
pass
|
|
70
|
+
return result
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
@app.post("/audit/github")
|
|
74
|
+
def audit_github(req: AuditGitHubRequest):
|
|
75
|
+
"""Provide repo URL; returns detected stack, missing items, migration suggestions."""
|
|
76
|
+
if not req.repo_url or len(req.repo_url) > 2048:
|
|
77
|
+
raise HTTPException(status_code=400, detail="Invalid repo URL")
|
|
78
|
+
return github_audit(req.repo_url)
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
if __name__ == "__main__":
|
|
82
|
+
import uvicorn
|
|
83
|
+
uvicorn.run(app, host="0.0.0.0", port=8000)
|