Skip to content

Commit 12d7b7a

Browse files
committed
Updated
1 parent 5962cf4 commit 12d7b7a

5 files changed

Lines changed: 181 additions & 31 deletions

File tree

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,7 @@ RED-EYE-LEAD-HUNTER
163163
├── ⚙️ .gitignore
164164
├── 📝 HOST.md
165165
├── 📝 README.md
166+
├── 📝 RUN.md
166167
├── 📄 bun.lockb
167168
├── ⚙️ components.json
168169
├── 📄 eslint.config.js

RUN.md

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
# 🚀 Running RED-EYE LEAD HUNTER Locally
2+
3+
This guide will help you set up and run the **RED-EYE LEAD HUNTER** project on your local machine for development and testing.
4+
5+
---
6+
7+
## 🛠️ Prerequisites
8+
9+
Before you begin, ensure you have the following installed:
10+
- [Node.js](https://nodejs.org/) (v18.0.0 or higher)
11+
- [npm](https://www.npmjs.com/) or [Bun](https://bun.sh/) (Recommended for faster installs)
12+
- [Google Chrome](https://www.google.com/chrome/) (For testing the extension)
13+
14+
---
15+
16+
## 💻 1. Running the Web App (Landing Page & Dashboard)
17+
18+
The web application is built with **React**, **Vite**, and **Tailwind CSS**.
19+
20+
### Step 1: Install Dependencies
21+
Open your terminal in the root directory of the project and run:
22+
23+
```bash
24+
# Using Bun (Recommended)
25+
bun install
26+
27+
# OR using npm
28+
npm install
29+
```
30+
31+
### Step 2: Start the Development Server
32+
Run the following command to start the local development server:
33+
34+
```bash
35+
# Using Bun
36+
bun run dev
37+
38+
# OR using npm
39+
npm run dev
40+
```
41+
42+
Once started, you can access the application at:
43+
👉 **[http://localhost:5173](http://localhost:5173)**
44+
45+
---
46+
47+
## 👁️ 2. Loading the Chrome Extension
48+
49+
The extension interacts directly with Google Maps to harvest leads.
50+
51+
1. Open **Google Chrome**.
52+
2. Navigate to `chrome://extensions/`.
53+
3. Enable **"Developer mode"** (toggle switch in the top-right corner).
54+
4. Click the **"Load unpacked"** button.
55+
5. Select the `extension` folder inside the `RED-EYE-LEAD-HUNTER` directory.
56+
6. The **RED EYE** icon should now appear in your extension toolbar.
57+
58+
> [!TIP]
59+
> To test the extension, go to [Google Maps](https://www.google.com/maps) and search for a business. The RED EYE panel will automatically inject into the page.
60+
61+
---
62+
63+
## 🏗️ 3. Building for Production
64+
65+
To create a production-ready build of the web application:
66+
67+
```bash
68+
# Using Bun
69+
bun run build
70+
71+
# OR using npm
72+
npm run build
73+
```
74+
75+
The optimized files will be generated in the `dist/` directory.
76+
77+
---
78+
79+
## 🧪 4. Running Tests
80+
81+
The project uses **Vitest** for unit testing.
82+
83+
```bash
84+
# Run tests once
85+
npm run test
86+
87+
# Run tests in watch mode
88+
npm run test:watch
89+
```
90+
91+
---
92+
93+
## 📁 Project Structure Recap
94+
95+
- `src/`: React application source code.
96+
- `extension/`: Chrome Extension source code (Background, Content, Popup).
97+
- `public/`: Static assets for the web app.
98+
- `dist/`: Production build output (generated after running build).
99+
100+
---
101+
102+
## 🆘 Troubleshooting
103+
104+
- **Port 5173 is busy**: Vite will automatically try the next available port (e.g., 5174). Check the terminal output for the correct URL.
105+
- **Extension not appearing**: Ensure you are on a `google.com/maps` URL, as the content script is configured to only run there.
106+
- **Dependencies issues**: Try deleting `node_modules` and the lock file (`bun.lockb` or `package-lock.json`) and reinstalling.
107+
108+
---
109+
110+
**Developed by MD SAMIUR RAHMAN TANIM**
111+
*Vibe Coding for Efficiency*

extension/content.js

Lines changed: 46 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,32 @@
2121
const rand = (min, max) => Math.floor(Math.random() * (max - min) + min);
2222
const qs = (root, sel) => { try { return root.querySelector(sel); } catch { return null; } };
2323
const qsa = (root, sel) => { try { return Array.from(root.querySelectorAll(sel)); } catch { return []; } };
24+
const cleanUrl = (url) => {
25+
if (!url) return "";
26+
try {
27+
const u = new URL(url);
28+
if (u.hostname.includes("google.") && u.pathname === "/url") {
29+
return u.searchParams.get("q") || url;
30+
}
31+
} catch (e) {}
32+
return url;
33+
};
34+
const isUnrelated = (url, label) => {
35+
if (!url) return true;
36+
const lowerUrl = url.toLowerCase();
37+
const lowerLabel = (label || "").toLowerCase();
38+
const unrelatedPatterns = [
39+
"google.com/searchviewer",
40+
"google.com/maps/reserve",
41+
"eat.chownow.com",
42+
"opentable.com",
43+
"allmenus.com",
44+
"fooddiscoveryapp.com"
45+
];
46+
if (unrelatedPatterns.some((p) => lowerUrl.includes(p))) return true;
47+
if (lowerLabel.includes("menu") || lowerLabel.includes("order online") || lowerLabel.includes("reservation")) return true;
48+
return false;
49+
};
2450

2551
function getFeed() {
2652
return (
@@ -96,9 +122,9 @@
96122

97123
const websiteEl =
98124
qs(card, 'a[data-value="Website"]') ||
99-
qs(card, 'a[aria-label^="Visit"]') ||
100125
qs(card, 'a[aria-label*="website" i]');
101-
const website = websiteEl?.href || "";
126+
let website = cleanUrl(websiteEl?.href || "");
127+
if (isUnrelated(website, websiteEl?.getAttribute("aria-label"))) website = "";
102128

103129
if (!name) return null;
104130
const { city, country } = parseAddress(address);
@@ -132,15 +158,16 @@
132158
if (!link) return lead;
133159

134160
link.click();
135-
// Wait for the detail panel to load (heading appears)
161+
// Wait for the detail panel to load and update to the NEW business (avoid stale data)
136162
let panelEl = null;
137-
for (let i = 0; i < 25; i++) {
138-
await sleep(180);
163+
for (let i = 0; i < 30; i++) {
164+
await sleep(200);
139165
panelEl =
140166
document.querySelector('div[role="main"][aria-label]') ||
141-
document.querySelector('div.m6QErb.DxyBCb');
142-
const heading = panelEl && qs(panelEl, "h1");
143-
if (heading && heading.textContent.trim()) break;
167+
document.querySelector('div.m6QErb.DxyBCb') ||
168+
document.querySelector('div[role="region"][aria-label]');
169+
const heading = panelEl && qs(panelEl, "h1")?.textContent?.trim();
170+
if (heading && (heading.includes(lead.name) || lead.name.includes(heading))) break;
144171
}
145172
if (!panelEl) return lead;
146173

@@ -168,10 +195,18 @@
168195
}
169196

170197
// Website (authority link)
171-
const siteBtn = qs(panelEl, 'a[data-item-id="authority"]') || qs(panelEl, 'a[aria-label^="Website"]');
198+
const siteBtn =
199+
qs(panelEl, 'a[data-item-id="authority"]') ||
200+
qs(panelEl, 'a[aria-label^="Website:"]') ||
201+
qs(panelEl, 'a[aria-label="Open website"]') ||
202+
qs(panelEl, 'a[aria-label^="Website"]');
172203
if (siteBtn?.href) {
173-
lead.website = siteBtn.href;
174-
lead.hasWebsite = "Yes";
204+
const url = cleanUrl(siteBtn.href);
205+
const label = siteBtn.getAttribute("aria-label");
206+
if (url && !url.includes("google.com/maps") && !isUnrelated(url, label)) {
207+
lead.website = url;
208+
lead.hasWebsite = "Yes";
209+
}
175210
}
176211

177212
// Scan all anchors in the panel for socials / whatsapp / mailto

index.html

Lines changed: 23 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,28 @@
11
<!doctype html>
22
<html lang="en">
3-
<head>
4-
<meta charset="UTF-8" />
5-
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
6-
<title>RED EYE — Google Maps lead scraper to CSV</title>
7-
<meta name="description" content="RED EYE is a free Chrome extension that scrapes Google Maps business leads (name, phone, website, rating) and exports them to CSV." />
8-
<meta name="author" content="RED EYE" />
9-
<link rel="icon" href="/favicon.png" type="image/png" />
103

11-
<meta property="og:title" content="RED EYE — Google Maps lead scraper" />
12-
<meta property="og:description" content="Scrape Google Maps leads and export to CSV in one click." />
13-
<meta property="og:type" content="website" />
14-
<meta property="og:image" content="https://lovable.dev/opengraph-image-p98pqg.png" />
4+
<head>
5+
<meta charset="UTF-8" />
6+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
7+
<title>RED EYE — Google Maps lead scraper to CSV</title>
8+
<meta name="description"
9+
content="RED EYE is a free Chrome extension that scrapes Google Maps business leads (name, phone, website, rating) and exports them to CSV." />
10+
<meta name="author" content="RED EYE" />
11+
<link rel="icon" href="/favicon.png" type="image/png" />
1512

16-
<meta name="twitter:card" content="summary_large_image" />
17-
<meta name="twitter:site" content="@Lovable" />
18-
<meta name="twitter:image" content="https://lovable.dev/opengraph-image-p98pqg.png" />
19-
</head>
13+
<meta property="og:title" content="RED EYE — Google Maps lead scraper" />
14+
<meta property="og:description" content="Scrape Google Maps leads and export to CSV in one click." />
15+
<meta property="og:type" content="website" />
16+
<meta property="og:image" content="https://redeye.exploitinject.dev/" />
2017

21-
<body>
22-
<div id="root"></div>
23-
<script type="module" src="/src/main.tsx"></script>
24-
</body>
25-
</html>
18+
<meta name="twitter:card" content="summary_large_image" />
19+
<meta name="twitter:site" content="@ExploitInject" />
20+
<meta name="twitter:image" content="https://redeye.exploitinject.dev/" />
21+
</head>
22+
23+
<body>
24+
<div id="root"></div>
25+
<script type="module" src="/src/main.tsx"></script>
26+
</body>
27+
28+
</html>

public/red-eye.zip

281 Bytes
Binary file not shown.

0 commit comments

Comments
 (0)