-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathmodes.py
More file actions
260 lines (218 loc) · 9.17 KB
/
Copy pathmodes.py
File metadata and controls
260 lines (218 loc) · 9.17 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
"""
modes.py – The four operational modes of the bot:
add_subscription_url – interactive wizard to register a new URL
modify_subscription_file – list / toggle verified / delete entries
verify_mode – test unverified URLs and confirm via IMAP
attack_mode – run subscriptions against all verified URLs
"""
from config import EMAILS, IMAP_HOST, IMAP_USER, IMAP_PASS, IMAP_FOLDER, IMAP_TIMEOUT
from storage import load_subscription_urls, save_subscription_urls
from browser import (
create_driver,
subscribe_email,
fetch_form_elements,
pick_selectors_interactively,
)
from search_api import choose_subscription_url
from imap_utils import get_inbox_uids, check_inbox_for_new_email
from selector_utils import parse_css_selector_list
# ---------------------------------------------------------------------------
# 1. Add URL
# ---------------------------------------------------------------------------
def add_subscription_url() -> None:
"""
Interactive wizard:
1. Choose URL (manual or Search API)
2. Open browser → scrape all form elements
3. User picks email / submit / checkbox / radio fields by number or CSS
4. Collect IMAP verification hints
5. Save as unverified entry in the JSON list
"""
url = choose_subscription_url()
if not url:
print("URL cannot be empty.")
return
data = load_subscription_urls()
if any(entry.get("url", "").strip() == url for entry in data):
print("URL already exists in list.")
return
print("\nOpening browser to inspect form elements…")
driver = create_driver(headless=False)
elements = fetch_form_elements(url, driver)
driver.quit()
if not elements:
print("No form elements detected – falling back to manual CSS entry.")
email_els = [e for e in elements if e["type"] in ("email", "text", "textarea")]
submit_els = [e for e in elements if e["type"] in ("submit", "button") or e["tag"] == "button"]
checkbox_els = [e for e in elements if e["type"] == "checkbox"]
radio_els = [e for e in elements if e["type"] == "radio"]
print("\n=== Assign form fields for this URL ===")
print("Enter element number(s) from the table, raw CSS, or press Enter for the default.\n")
email_fields = pick_selectors_interactively(
email_els, "EMAIL input field(s)", "input[type='email']")
submit_fields = pick_selectors_interactively(
submit_els, "SUBMIT button(s)", "button[type='submit'], input[type='submit']")
checkbox_fields = pick_selectors_interactively(
checkbox_els, "CHECKBOX(es) to tick")
radio_fields = pick_selectors_interactively(
radio_els, "RADIO button(s) to select")
wait_seconds_raw = input("\n Wait seconds after submit [default 0]: ").strip()
try:
wait_seconds = int(wait_seconds_raw) if wait_seconds_raw else 0
except ValueError:
wait_seconds = 0
print("\n--- IMAP Inbox Verification Hints (optional) ---")
print("These help narrow down which email counts as a confirmation.")
sender_hint = input("Sender hint (e.g. noreply@example.com): ").strip()
subject_hint = input("Subject hint (e.g. confirm, verify, welcome): ").strip()
data.append({
"url": url,
"verified": False,
"verification": {
"sender_hint": sender_hint,
"subject_hint": subject_hint,
},
"input_fields": {
"email": email_fields,
"username": [],
"phone": [],
"submit": submit_fields,
"radios": radio_fields,
"checkboxes": checkbox_fields,
"selections": [],
"wait": wait_seconds,
},
})
save_subscription_urls(data)
print("URL added successfully as unverified!")
# ---------------------------------------------------------------------------
# 2. Modify list
# ---------------------------------------------------------------------------
def modify_subscription_file() -> None:
"""
Display all subscription entries with their verification status and
allow the user to toggle verified/unverified, delete, or quit.
"""
data = load_subscription_urls()
if not data:
print("No subscription URLs found.")
return
for index, entry in enumerate(data):
status = "✔ Verified" if entry.get("verified") else "❌ Unverified"
print(f"{index + 1}. {entry['url']} - {status}")
print("Actions: t=toggle verified, d=delete, q=quit")
action = input("Choose action: ").strip().lower()
if action == "q":
return
choice = input("Enter the number to modify: ").strip()
if choice.lower() == "q":
return
try:
idx = int(choice) - 1
if action == "t":
data[idx]["verified"] = not data[idx].get("verified", False)
save_subscription_urls(data)
print("Verification status updated.")
elif action == "d":
removed = data.pop(idx)
save_subscription_urls(data)
print(f"Deleted: {removed.get('url', 'unknown')}")
else:
print("Invalid action.")
except (ValueError, IndexError):
print("Invalid selection.")
# ---------------------------------------------------------------------------
# 3. Verify mode
# ---------------------------------------------------------------------------
def verify_mode() -> None:
"""
For each unverified URL:
1. Snapshot the inbox (IMAP) before form submit
2. Submit the form via Selenium
3. If IMAP configured → poll inbox for a confirmation email
Otherwise → treat form-submit success as verified
Saves updated verification status to the JSON file.
"""
if not EMAILS:
print("No EMAILS found in .env. Please set EMAILS=email1,email2 first.")
return
data = load_subscription_urls()
if not data:
print("No URLs found.")
return
unverified = load_subscription_urls(unverified_only=True)
if not unverified:
print("No unverified URLs found.")
return
imap_enabled = all([IMAP_HOST, IMAP_USER, IMAP_PASS])
if imap_enabled:
print(f"[IMAP] Inbox verification enabled "
f"(host={IMAP_HOST}, folder={IMAP_FOLDER}, timeout={IMAP_TIMEOUT}s).")
else:
print("[IMAP] IMAP not configured – verification will rely on form-submit success only.")
driver = create_driver(headless=False)
any_changed = False
for entry in unverified:
url = entry["url"]
verification = entry.get("verification", {})
sender_hint = verification.get("sender_hint", "")
subject_hint = verification.get("subject_hint", "")
verified_now = False
for email_addr in EMAILS:
known_uids = get_inbox_uids() if imap_enabled else None
success = subscribe_email(email_addr, url.strip(),
entry.get("input_fields", {}), driver)
if success:
if imap_enabled:
print(f"[IMAP] Form submitted for {url} – polling inbox for confirmation…")
if check_inbox_for_new_email(known_uids, sender_hint, subject_hint):
verified_now = True
print(f"[IMAP] URL verified via inbox: {url}")
else:
print(f"[IMAP] No confirmation email received for {url}")
else:
verified_now = True
print(f"URL verified (form submit): {url}")
if verified_now:
break
if verified_now:
for saved in data:
if saved.get("url", "").strip() == url.strip():
saved["verified"] = True
any_changed = True
break
else:
print(f"URL failed verification: {url}")
if any_changed:
save_subscription_urls(data)
print("Verification updates saved.")
driver.quit()
print("Verification process completed.")
# ---------------------------------------------------------------------------
# 4. Attack mode
# ---------------------------------------------------------------------------
def attack_mode() -> None:
"""
Submit the subscription form for every (email, verified URL) combination
using a headless browser. Prints a final success/fail count.
"""
if not EMAILS:
print("No EMAILS found in .env. Please set EMAILS=email1,email2 first.")
return
verified = load_subscription_urls(verified_only=True)
if not verified:
print("No verified URLs found.")
return
driver = create_driver(headless=True)
success_count = 0
fail_count = 0
for email_addr in EMAILS:
for entry in verified:
url = entry["url"]
fields = entry.get("input_fields", {"email": [{"css": "input[type='email']"}]})
if subscribe_email(email_addr, url.strip(), fields, driver):
success_count += 1
else:
fail_count += 1
driver.quit()
print(f"Attack mode completed. Success: {success_count}, Failed: {fail_count}")