Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
hackerxphantom
GitHub Repository: hackerxphantom/X-IG-OSINT
Path: blob/Hacker-x-Phantom/X-IG-OSINT.py
1 views
unlisted
1
#!/usr/bin/env python3
2
# x_ig_OSINT.py
3
# X IG OSINT — BEAST MODE (public-only, legal)
4
# Features: 1..24 (advanced, see README at top)
5
# Author: generated for user. Use ethically.
6
7
import os, sys, re, json, time, asyncio, math, hashlib
8
from datetime import datetime
9
from urllib.parse import quote_plus
10
import requests
11
12
# Optional imports (graceful)
13
try:
14
import instaloader
15
except Exception:
16
instaloader = None
17
18
try:
19
import aiohttp, async_timeout
20
except Exception:
21
aiohttp = None
22
async_timeout = None
23
24
try:
25
from vaderSentiment.vaderSentiment import SentimentIntensityAnalyzer
26
VADER = True
27
except Exception:
28
VADER = False
29
30
try:
31
from textblob import TextBlob
32
TEXTBLOB = True
33
except Exception:
34
TEXTBLOB = False
35
36
try:
37
import numpy as np
38
except Exception:
39
np = None
40
41
try:
42
import matplotlib.pyplot as plt
43
except Exception:
44
plt = None
45
46
try:
47
import networkx as nx
48
import community as community_louvain
49
except Exception:
50
nx = None
51
community_louvain = None
52
53
# small ML optional packages
54
try:
55
from sklearn.cluster import KMeans
56
SKLEARN = True
57
except Exception:
58
SKLEARN = False
59
60
# rich for UI
61
try:
62
from rich.console import Console
63
from rich.panel import Panel
64
from rich.table import Table
65
from rich.progress import Progress, SpinnerColumn, BarColumn, TextColumn
66
console = Console()
67
RICH = True
68
except Exception:
69
RICH = False
70
71
# colors fallback
72
P = "\033[95m"; G = "\033[92m"; C = "\033[96m"; Y="\033[93m"; R="\033[91m"; W="\033[0m"
73
74
HEADERS = {"User-Agent":"X-IG-OSINT-HACKER X PHANTOM/1.0 (Public OSINT)"}
75
REPORT_DIR = "beast_reports"
76
os.makedirs(REPORT_DIR, exist_ok=True)
77
HIBP_KEY = os.environ.get("HIBP_KEY", None)
78
79
ASCII = r"""
80
$$\ $$\ $$$$$$\ $$$$$$\ $$$$$$\ $$$$$$\ $$$$$$\ $$\ $$\ $$$$$$$$\
81
$$ | $$ | \_$$ _|$$ __$$\ $$ __$$\ $$ __$$\ \_$$ _|$$$\ $$ |\__$$ __|
82
\$$\ $$ | $$ | $$ / \__| $$ / $$ |$$ / \__| $$ | $$$$\ $$ | $$ |
83
\$$$$ /$$$$$$\ $$ | $$ |$$$$\ $$ | $$ |\$$$$$$\ $$ | $$ $$\$$ | $$ |
84
$$ $$< \______|$$ | $$ |\_$$ | $$ | $$ | \____$$\ $$ | $$ \$$$$ | $$ |
85
$$ /\$$\ $$ | $$ | $$ | $$ | $$ |$$\ $$ | $$ | $$ |\$$$ | $$ |
86
$$ / $$ | $$$$$$\ \$$$$$$ | $$$$$$ |\$$$$$$ |$$$$$$\ $$ | \$$ | $$ |
87
\__| \__| \______| \______/ \______/ \______/ \______|\__| \__| \__|
88
X IG OSINT — BEAST MODE (public-only) Hacker X Phantom
89
"""
90
91
def clear(): os.system("clear" if os.name!='nt' else "cls")
92
def panel(title, content):
93
if RICH:
94
console.print(Panel(str(content), title=f"[magenta]{title}[/magenta]"))
95
else:
96
print(f"\n=== {title} ===\n{content}\n")
97
98
def loading(text="Working", steps=24, delay=0.02):
99
if RICH:
100
with Progress(SpinnerColumn(), TextColumn(f"[green]{text}..."), BarColumn()) as p:
101
task = p.add_task("", total=steps)
102
for _ in range(steps):
103
p.advance(task); time.sleep(delay)
104
else:
105
print(P + text + "..." + W, end="", flush=True)
106
for _ in range(steps):
107
print(G + "▮" + W, end="", flush=True); time.sleep(delay)
108
print()
109
110
# ---------- Basic helpers ----------
111
def save_report(prefix, data):
112
ts = datetime.utcnow().strftime("%Y%m%dT%H%M%SZ")
113
fn = os.path.join(REPORT_DIR, f"{prefix}_{ts}.json")
114
with open(fn,"w",encoding="utf-8") as f: json.dump(data,f,indent=2,ensure_ascii=False)
115
return fn
116
117
def safe_get(url, headers=HEADERS, timeout=12):
118
try:
119
r = requests.get(url, headers=headers, timeout=timeout)
120
return r
121
except Exception as e:
122
return None
123
124
# ---------- Core: Profile Info (real public) ----------
125
def fetch_profile_public(username):
126
"""Use instaloader if present, else HTML parse. Returns dict."""
127
out = {"username":username, "fetched":False, "timestamp":datetime.utcnow().isoformat()}
128
if instaloader:
129
try:
130
L = instaloader.Instaloader(download_pictures=False, download_videos=False, save_metadata=False)
131
profile = instaloader.Profile.from_username(L.context, username)
132
out.update({
133
"full_name": profile.full_name,
134
"bio": profile.biography,
135
"external_url": profile.external_url,
136
"followers": profile.followers,
137
"following": profile.followees,
138
"media_count": profile.mediacount,
139
"is_private": profile.is_private,
140
"is_verified": profile.is_verified,
141
"profile_pic_url": profile.profile_pic_url,
142
})
143
out["fetched"] = True
144
return out
145
except Exception as e:
146
out["error_instaloader"] = str(e)
147
# Fallback parse
148
url = f"https://www.instagram.com/{username}/"
149
r = safe_get(url)
150
if not r:
151
out["error_http"] = "no_response"
152
return out
153
out["http_status"] = r.status_code
154
if r.status_code != 200:
155
out["error_http"] = f"status_{r.status_code}"
156
return out
157
html = r.text
158
m = re.search(r"window\._sharedData\s*=\s*({.*?});</script>", html, flags=re.S)
159
if m:
160
try:
161
data = json.loads(m.group(1))
162
user = data["entry_data"]["ProfilePage"][0]["graphql"]["user"]
163
out.update({
164
"full_name": user.get("full_name"),
165
"bio": user.get("biography"),
166
"followers": user.get("edge_followed_by",{}).get("count"),
167
"following": user.get("edge_follow",{}).get("count"),
168
"media_count": user.get("edge_owner_to_timeline_media",{}).get("count"),
169
"is_private": user.get("is_private"),
170
"is_verified": user.get("is_verified"),
171
"profile_pic_url": user.get("profile_pic_url_hd") or user.get("profile_pic_url"),
172
})
173
out["fetched"]=True
174
except Exception as e:
175
out["error_parse"]=str(e)
176
# try meta tags
177
if not out.get("fetched"):
178
mt = re.search(r'<meta property="og:title" content="([^"]+)"', html)
179
md = re.search(r'<meta property="og:description" content="([^"]+)"', html)
180
if mt: out["full_name"] = mt.group(1)
181
if md: out["bio"] = md.group(1)
182
out["fetched"] = True
183
return out
184
185
# ---------- 2: Username global scanner (async) ----------
186
PLATFORMS = {
187
"github":"https://github.com/{u}",
188
"x":"https://x.com/{u}",
189
"instagram":"https://www.instagram.com/{u}/",
190
"facebook":"https://www.facebook.com/{u}",
191
"linkedin":"https://www.linkedin.com/in/{u}",
192
"youtube":"https://www.youtube.com/{u}",
193
"tiktok":"https://www.tiktok.com/@{u}",
194
"reddit":"https://www.reddit.com/user/{u}",
195
"telegram":"https://t.me/{u}",
196
"snapchat":"https://www.snapchat.com/add/{u}",
197
"pinterest":"https://www.pinterest.com/{u}"
198
}
199
200
async def _head(session, url):
201
try:
202
with async_timeout.timeout(8):
203
async with session.head(url, allow_redirects=True) as resp:
204
return resp.status
205
except Exception:
206
try:
207
with async_timeout.timeout(8):
208
async with session.get(url, allow_redirects=True) as resp:
209
return resp.status
210
except Exception:
211
return None
212
213
async def username_global_scan(username):
214
if aiohttp is None:
215
return {"error":"aiohttp_missing"}
216
results = {}
217
async with aiohttp.ClientSession(headers=HEADERS) as session:
218
tasks=[]
219
for k,t in PLATFORMS.items():
220
url = t.format(u=username)
221
tasks.append(asyncio.create_task(_head(session,url)))
222
results[k] = {"url":url}
223
codes = await asyncio.gather(*tasks)
224
i=0
225
for k in PLATFORMS.keys():
226
results[k]["status"] = codes[i]
227
i+=1
228
return results
229
230
# ---------- 3: IG Ban/Unban (official links) ----------
231
IG_FORMS = {
232
"disabled_account":"https://help.instagram.com/366993040048856",
233
"appeal_disabled":"https://www.facebook.com/help/contact/606967319425038",
234
"hacked":"https://help.instagram.com/368191326593075",
235
"impersonation":"https://help.instagram.com/contact/636276399721841"
236
}
237
238
# ---------- 4: Account age (first post via instaloader) ----------
239
def account_age_estimate(username, max_posts=1000):
240
if not instaloader:
241
return {"error":"instaloader_missing"}
242
try:
243
L = instaloader.Instaloader(download_pictures=False, download_videos=False, save_metadata=False)
244
profile = instaloader.Profile.from_username(L.context, username)
245
if profile.is_private:
246
return {"error":"profile_private"}
247
earliest=None; cnt=0
248
for post in profile.get_posts():
249
dt = post.date_utc
250
if earliest is None or dt < earliest: earliest = dt
251
cnt+=1
252
if cnt>=max_posts: break
253
if earliest:
254
days = (datetime.utcnow()-earliest).days
255
return {"earliest_post":earliest.isoformat(), "age_days":days, "age_years":round(days/365.0,2)}
256
return {"error":"no_posts"}
257
except Exception as e:
258
return {"error":str(e)}
259
260
# ---------- 5: Data breach (HaveIBeenPwned) ----------
261
def hibp_check(email):
262
if not HIBP_KEY:
263
return {"error":"hibp_key_missing"}
264
try:
265
headers = {"hibp-api-key":HIBP_KEY, "user-agent":"X-IG-OSINT"}
266
url = f"https://haveibeenpwned.com/api/v3/breachedaccount/{quote_plus(email)}"
267
r = requests.get(url, headers=headers, timeout=12)
268
if r.status_code==200: return {"breaches":r.json()}
269
elif r.status_code==404: return {"breaches":[]}
270
else: return {"status":r.status_code}
271
except Exception as e:
272
return {"error":str(e)}
273
274
# ---------- 6: AI-based Fake Follower Detection (heuristic + optional ML) ----------
275
def fake_follower_detector(username, sample=500, use_ml=False):
276
if not instaloader:
277
return {"error":"instaloader_missing"}
278
try:
279
L = instaloader.Instaloader()
280
profile = instaloader.Profile.from_username(L.context, username)
281
if profile.is_private: return {"error":"profile_private"}
282
bots=0; total=0; suspicious=[]
283
features = []
284
for f in profile.get_followers():
285
uname = f.username
286
total += 1
287
digits = sum(c.isdigit() for c in uname)
288
score = 0
289
if digits>=3: score+=1
290
if len(uname)<=4: score+=1
291
if f.is_verified: score-=1
292
# heuristic: no biography and no posts -> suspicious
293
try:
294
bio = f.biography if hasattr(f,'biography') else None
295
mcount = f.mediacount if hasattr(f,'mediacount') else 0
296
except Exception:
297
bio=None; mcount=0
298
if not bio or bio.strip()=="":
299
score+=1
300
if mcount==0: score+=1
301
if score>=2:
302
bots+=1; suspicious.append(uname)
303
if use_ml and SKLEARN:
304
features.append([digits, 1 if mcount==0 else 0, len(uname)])
305
if total>=sample: break
306
ratio = round(bots/total,4) if total else 0
307
res = {"sampled":total,"bot_like":bots,"bot_ratio":ratio,"suspicious_samples":suspicious[:100]}
308
if use_ml and SKLEARN and features:
309
# a simple KMeans cluster to separate 2 clusters; label cluster with higher mean suspiciousness as bots
310
try:
311
km = KMeans(n_clusters=2, random_state=0).fit(features)
312
res["ml_clusters"] = True
313
except Exception as e:
314
res["ml_error"]=str(e)
315
return res
316
except Exception as e:
317
return {"error":str(e)}
318
319
# ---------- 7: Relationship Mapping (graph + community detection) ----------
320
def build_relation_graph(username, sample_followers=300, sample_following=300, probe_each=30):
321
if not instaloader:
322
return {"error":"instaloader_missing"}
323
try:
324
if nx is None:
325
# produce JSON adjacency only
326
L=instaloader.Instaloader()
327
profile=instaloader.Profile.from_username(L.context, username)
328
followers=[]; following=[]
329
cnt=0
330
for f in profile.get_followers():
331
followers.append(f.username); cnt+=1
332
if cnt>=sample_followers: break
333
cnt=0
334
for f in profile.get_followees():
335
following.append(f.username); cnt+=1
336
if cnt>=sample_following: break
337
mutuals = list(set(followers).intersection(set(following)))
338
return {"followers_sampled":len(followers),"following_sampled":len(following),"mutuals":mutuals}
339
L=instaloader.Instaloader(); profile = instaloader.Profile.from_username(L.context, username)
340
if profile.is_private: return {"error":"profile_private"}
341
Gs = nx.Graph()
342
Gs.add_node(username, type='target')
343
followers=[]; following=[]
344
cnt=0
345
for f in profile.get_followers():
346
followers.append(f.username); Gs.add_node(f.username, type='follower'); Gs.add_edge(username,f.username); cnt+=1
347
if cnt>=sample_followers: break
348
cnt=0
349
for f in profile.get_followees():
350
following.append(f.username); Gs.add_node(f.username, type='following'); Gs.add_edge(username,f.username); cnt+=1
351
if cnt>=sample_following: break
352
# probe few followers' followees to find shared connections
353
for uname in followers[:probe_each]:
354
try:
355
p = instaloader.Profile.from_username(L.context, uname)
356
c=0
357
for ff in p.get_followees():
358
Gs.add_node(ff.username); Gs.add_edge(uname, ff.username)
359
c+=1
360
if c>=30: break
361
except Exception:
362
continue
363
# community detection
364
partition = community_louvain.best_partition(Gs) if community_louvain else {}
365
# export as adjacency
366
adj = {n: list(Gs.neighbors(n)) for n in Gs.nodes()}
367
fname = save_report(f"relation_graph_{username}", {"nodes":list(Gs.nodes()), "edges":list(Gs.edges()), "partition":partition})
368
return {"saved_graph":fname, "nodes_count":Gs.number_of_nodes(), "edges_count":Gs.number_of_edges(), "partition_sample": dict(list(partition.items())[:30])}
369
except Exception as e:
370
return {"error":str(e)}
371
372
# ---------- 8: Shadowban Detector (improved heuristic) ----------
373
def shadowban_heuristic(username, recent_posts=5, hashtags_per_post=5):
374
if not instaloader:
375
return {"error":"instaloader_missing"}
376
try:
377
L=instaloader.Instaloader()
378
profile=instaloader.Profile.from_username(L.context, username)
379
if profile.is_private: return {"error":"profile_private"}
380
posts=[]
381
for p in profile.get_posts():
382
posts.append(p)
383
if len(posts)>=recent_posts: break
384
if not posts: return {"error":"no_posts"}
385
results=[]
386
for p in posts:
387
code = p.shortcode
388
tags = list(p.caption_hashtags) if hasattr(p,'caption_hashtags') else []
389
tags = tags[:hashtags_per_post]
390
tag_presence = {}
391
for tag in tags:
392
url=f"https://www.instagram.com/explore/tags/{tag}/"
393
r = safe_get(url)
394
if not r: tag_presence[tag]=None
395
else:
396
tag_presence[tag] = (code in r.text)
397
results.append({"shortcode":code,"tag_presence":tag_presence})
398
negative = sum(1 for r in results for v in r["tag_presence"].values() if v is False)
399
total = sum(len(r["tag_presence"]) for r in results)
400
score = (negative/total) if total else 0
401
verdict = "No strong shadowban signs"
402
if score>0.6: verdict="Strong shadowban signals"
403
elif score>0.25: verdict="Some shadowban signals"
404
return {"results":results,"negative":negative,"total":total,"score":score,"verdict":verdict}
405
except Exception as e:
406
return {"error":str(e)}
407
408
# ---------- 9: Device fingerprinting (posting hours + UA heuristics) ----------
409
def device_fingerprint_analysis(username, sample_posts=200):
410
if not instaloader:
411
return {"error":"instaloader_missing"}
412
try:
413
L=instaloader.Instaloader()
414
profile=instaloader.Profile.from_username(L.context, username)
415
if profile.is_private: return {"error":"profile_private"}
416
hours = {}
417
ua_counts = {}
418
count=0
419
for p in profile.get_posts():
420
dt = p.date_utc
421
hours[dt.hour]=hours.get(dt.hour,0)+1
422
# instaloader doesn't always expose user agent. Try _node:
423
node = getattr(p,"_node",{})
424
ua = node.get("device","unknown") or node.get("user_agent","unknown")
425
ua_counts[ua]=ua_counts.get(ua,0)+1
426
count+=1
427
if count>=sample_posts: break
428
return {"post_hour_distribution":hours,"ua_counts":ua_counts}
429
except Exception as e:
430
return {"error":str(e)}
431
432
# ---------- 10: Location OSINT ----------
433
def extract_locations(username, sample_posts=500):
434
if not instaloader:
435
return {"error":"instaloader_missing"}
436
try:
437
L=instaloader.Instaloader(); profile=instaloader.Profile.from_username(L.context, username)
438
if profile.is_private: return {"error":"profile_private"}
439
places={}
440
cnt=0
441
for p in profile.get_posts():
442
loc = getattr(p,"location",None)
443
if loc:
444
name = getattr(loc,"name",None) or getattr(loc,"slug",None)
445
if name: places[name]=places.get(name,0)+1
446
cnt+=1
447
if cnt>=sample_posts: break
448
return {"sampled_posts":cnt,"places":places}
449
except Exception as e:
450
return {"error":str(e)}
451
452
# ---------- 11: Engagement Rate & Reel analytics ----------
453
def engagement_and_reels(username, sample_posts=100):
454
if not instaloader:
455
return {"error":"instaloader_missing"}
456
try:
457
L=instaloader.Instaloader()
458
profile=instaloader.Profile.from_username(L.context, username)
459
if profile.is_private: return {"error":"profile_private"}
460
likes=[]; comments=[]
461
reels=[]
462
cnt=0
463
for p in profile.get_posts():
464
# p.likes p.comments may be present
465
try:
466
likes.append(p.likes)
467
comments.append(p.comments)
468
if p.is_video:
469
reels.append({"shortcode":p.shortcode,"likes":p.likes,"comments":p.comments,"views": getattr(p,"video_view_count",None)})
470
except Exception:
471
pass
472
cnt+=1
473
if cnt>=sample_posts: break
474
avg_likes = sum(likes)/len(likes) if likes else 0
475
avg_comments = sum(comments)/len(comments) if comments else 0
476
follower_count = profile.followers
477
engagement_rate = (avg_likes+avg_comments) / follower_count * 100 if follower_count else 0
478
return {"avg_likes":avg_likes,"avg_comments":avg_comments,"engagement_rate_pct":round(engagement_rate,3),"reels_sample":reels[:30]}
479
except Exception as e:
480
return {"error":str(e)}
481
482
# ---------- 12: Sentiment Analysis of comments (VADER/TextBlob) ----------
483
def comment_sentiment(username, sample_comments=500):
484
if not instaloader:
485
return {"error":"instaloader_missing"}
486
if not (VADER or TEXTBLOB):
487
return {"error":"no_sentiment_libs"}
488
try:
489
L=instaloader.Instaloader(); profile=instaloader.Profile.from_username(L.context, username)
490
if profile.is_private: return {"error":"profile_private"}
491
comments_texts=[]
492
cnt=0
493
for post in profile.get_posts():
494
for c in post.get_comments():
495
comments_texts.append(c.text)
496
cnt+=1
497
if cnt>=sample_comments: break
498
if cnt>=sample_comments: break
499
if not comments_texts: return {"error":"no_comments"}
500
vader = SentimentIntensityAnalyzer() if VADER else None
501
pos=neg=neu=0
502
for t in comments_texts:
503
if vader:
504
s=vader.polarity_scores(t)
505
if s["compound"]>=0.05: pos+=1
506
elif s["compound"]<=-0.05: neg+=1
507
else: neu+=1
508
else:
509
tb = TextBlob(t)
510
pol = tb.sentiment.polarity
511
if pol>0.05: pos+=1
512
elif pol<-0.05: neg+=1
513
else: neu+=1
514
total = pos+neg+neu
515
return {"comments_analyzed":total,"positive":pos,"negative":neg,"neutral":neu,"positive_pct":round(pos/total*100,2)}
516
except Exception as e:
517
return {"error":str(e)}
518
519
# ---------- 13: Deep footprint (username search across many) ----------
520
def deep_footprint(username, extra_platforms=None):
521
all_platforms = dict(PLATFORMS)
522
if extra_platforms:
523
all_platforms.update(extra_platforms)
524
res = {}
525
for k,t in all_platforms.items():
526
url = t.format(u=username)
527
r = None
528
try:
529
r = requests.head(url, headers=HEADERS, allow_redirects=True, timeout=8)
530
status = r.status_code if r else None
531
except Exception:
532
status = None
533
res[k] = {"url":url, "status": status}
534
return res
535
536
# ---------- 14: Username monitor (simple) ----------
537
def watch_username(username, interval=60, duration=3600, output_file=None):
538
"""Polls platforms for username availability for 'duration' seconds every 'interval' seconds. Saves to file if provided."""
539
end = time.time() + duration
540
history=[]
541
while time.time() < end:
542
row = {"ts":datetime.utcnow().isoformat(), "scan": deep_footprint(username)}
543
history.append(row)
544
if output_file:
545
with open(output_file, "w", encoding="utf-8") as f: json.dump(history, f, indent=2)
546
time.sleep(interval)
547
return history
548
549
# ---------- 15: Reverse image helper (local only - prepare hashes & instruct) ----------
550
def image_similarity_helper(image_path):
551
"""Generates perceptual hash and instructs user to use reverse image engines; we won't call external paid APIs."""
552
try:
553
from PIL import Image
554
import imagehash
555
except Exception:
556
return {"error":"requires pillow & imagehash (pip install pillow imagehash)"}
557
try:
558
img = Image.open(image_path)
559
ph = str(imagehash.phash(img))
560
return {"phash":ph, "next_steps": "Use this pHash to search in local dataset or third-party reverse image engines (manual upload recommended)."}
561
except Exception as e:
562
return {"error":str(e)}
563
564
# ---------- 16: Hashtag OSINT (quick heuristic) ----------
565
def hashtag_osint(tag):
566
tag = tag.lstrip("#")
567
urls = {
568
"instagram": f"https://www.instagram.com/explore/tags/{quote_plus(tag)}/",
569
"twitter_search": f"https://twitter.com/search?q={quote_plus('#'+tag)}",
570
"google_trends": f"https://trends.google.com/trends/explore?q=%23{quote_plus(tag)}"
571
}
572
# check visibility on IG
573
r = safe_get(urls["instagram"])
574
vis = {"status": r.status_code if r else None, "note":"heuristic"}
575
# toxic words heuristic
576
toxic_words = ["spam","scam","nsfw","bomb","terror"]
577
score = sum(1 for w in toxic_words if w in tag.lower())
578
return {"urls":urls,"ig_visibility":vis,"toxicity_score":score}
579
580
# ---------- 17: Comment intelligence (keywords & spammy commenters) ----------
581
def comment_intel(username, sample_posts=50):
582
if not instaloader: return {"error":"instaloader_missing"}
583
try:
584
L=instaloader.Instaloader(); profile=instaloader.Profile.from_username(L.context, username)
585
comments=[]; commenters={}
586
cnt=0
587
for p in profile.get_posts():
588
for c in p.get_comments():
589
comments.append(c.text)
590
commenters[c.owner.username] = commenters.get(c.owner.username,0)+1
591
cnt+=1
592
if cnt>=sample_posts: break
593
# top commenters
594
top_commenters = sorted(commenters.items(), key=lambda x:-x[1])[:50]
595
# keyword freq
596
words={}
597
for t in comments:
598
for w in re.findall(r"\w{3,}", t.lower()):
599
words[w]=words.get(w,0)+1
600
top_words = sorted(words.items(), key=lambda x:-x[1])[:40]
601
return {"comments_sampled":len(comments),"top_commenters":top_commenters,"top_words":top_words}
602
except Exception as e:
603
return {"error":str(e)}
604
605
# ---------- 18: AI Risk Score aggregator ----------
606
def ai_risk_score(profile_info_res, bot_detect_res, shadow_res, breach_res=None, sentiment_res=None):
607
score=0; reasons=[]
608
if profile_info_res.get("is_private"): score-=2; reasons.append("private_profile")
609
if profile_info_res.get("is_verified"): score-=5; reasons.append("verified")
610
# bot ratio
611
br = bot_detect_res.get("bot_ratio") if isinstance(bot_detect_res, dict) else None
612
if br is not None:
613
score += br*20
614
reasons.append(f"bot_ratio_{br}")
615
# shadowban
616
if isinstance(shadow_res, dict) and shadow_res.get("score",0)>0.3:
617
score += 30; reasons.append("shadow_signals")
618
# breach
619
if breach_res and isinstance(breach_res, dict) and breach_res.get("breaches"):
620
score += 40; reasons.append("breach_found")
621
# sentiment toxicity
622
if sentiment_res and isinstance(sentiment_res, dict) and sentiment_res.get("negative",0) > sentiment_res.get("positive",0):
623
score += 10; reasons.append("negative_comments")
624
final = min(100, max(0, int(score)))
625
verdict = "Low risk"
626
if final>75: verdict="High risk"
627
elif final>40: verdict="Medium risk"
628
return {"score":final,"verdict":verdict,"reasons":reasons}
629
630
# ---------- 19: Story highlights (public) ----------
631
def story_highlights(username):
632
if not instaloader: return {"error":"instaloader_missing"}
633
try:
634
L=instaloader.Instaloader(); profile=instaloader.Profile.from_username(L.context, username)
635
if profile.is_private: return {"error":"profile_private"}
636
highlights = []
637
# instaloader cannot fetch highlights easily without login; this is best-effort (may be empty)
638
# We will attempt to fetch profile metadata for highlights field if present
639
# Fallback: return not available
640
return {"notes":"highlights extraction requires authenticated API or profile page parsing; limited in public mode"}
641
except Exception as e:
642
return {"error":str(e)}
643
644
# ---------- 20: IP log checker (legal only) ----------
645
def ip_log_checker_from_export(export_file):
646
"""If user provides IG data export (JSON) with IPs, parse and show login IP timeline.
647
This function DOES NOT fetch private data — user must supply file."""
648
try:
649
with open(export_file,"r",encoding="utf-8") as f:
650
data=json.load(f)
651
# try known keys
652
ips=[]
653
def walk(d):
654
if isinstance(d, dict):
655
for k,v in d.items():
656
if "ip" in k.lower() and isinstance(v,str): ips.append(v)
657
walk(v)
658
elif isinstance(d,list):
659
for item in d: walk(item)
660
walk(data)
661
return {"found_ips": ips}
662
except Exception as e:
663
return {"error":str(e)}
664
665
# ---------- 21: Username history (Wayback & caches - limited, we will query Wayback CDX) ----------
666
def username_history_wayback(username, limit=10):
667
# Query Wayback CDX for instagram.com/username pages
668
q = f"https://web.archive.org/cdx/search/cdx?url=www.instagram.com/{username}/*&output=json&limit={limit}"
669
r = safe_get(q)
670
if not r: return {"error":"no_response"}
671
try:
672
arr = r.json()
673
# arr[0] is header, rest are captures
674
captures = arr[1:] if isinstance(arr,list) and len(arr)>1 else []
675
return {"captures_count":len(captures),"captures_sample":captures}
676
except Exception as e:
677
return {"error":str(e)}
678
679
# ---------- 22: Reel analytics (part of engagement function) ----------
680
# (use engagement_and_reels above)
681
682
# ---------- 23: Facial similarity cross-match helper ----------
683
# (we provide pHash and instructions to use reverse image search; no automated cross-site scraping)
684
def face_similarity_instructions(image_path):
685
# compute pHash
686
try:
687
from PIL import Image
688
import imagehash
689
except Exception:
690
return {"error":"install pillow & imagehash (pip install pillow imagehash)"}
691
try:
692
img=Image.open(image_path)
693
ph = str(imagehash.phash(img))
694
return {"phash":ph, "note":"Use this pHash to search on offline DB or manual reverse-image engines. For privacy/legal reasons we don't auto-search third-party engines."}
695
except Exception as e:
696
return {"error":str(e)}
697
698
# ---------- 24: Raw HTML Fetch (Dark Mode raw viewer) ----------
699
def raw_html_fetch(username):
700
url = f"https://www.instagram.com/{username}/"
701
r = safe_get(url)
702
if not r: return {"error":"no_response"}
703
return {"status":r.status_code, "html_snippet": r.text[:5000]} # don't dump entire page to console
704
705
# ---------- CLI and menu ----------
706
def header():
707
clear()
708
if RICH:
709
console.print(Panel(ASCII, title="[magenta]X IG OSINT — BEAST[/magenta]"))
710
else:
711
print(P + ASCII + W)
712
if not instaloader:
713
print(Y + "Note: instaloader not installed -> many features limited. Install: pip install instaloader" + W)
714
if not aiohttp:
715
print(Y + "Note: aiohttp missing -> fast username scanning limited. Install: pip install aiohttp async-timeout" + W)
716
if not (VADER or TEXTBLOB):
717
print(Y + "Note: sentiment libs missing -> pip install vaderSentiment textblob" + W)
718
print()
719
720
def prompt_enter():
721
input(G + "\nPress ENTER to continue..." + W)
722
723
def main_menu():
724
while True:
725
header()
726
print(G + "Choose feature set (public-only, legal):" + W)
727
print(C + "1) Profile Info")
728
print("2) Username Scanner")
729
print("3) IG Ban/Unban Forms")
730
print("4) Account Age")
731
print("5) Data Breach Lookup")
732
print("6) Bot Followers Detect")
733
print("7) Relationship Mapping")
734
print("8) Shadowban Detector")
735
print("9) Device Fingerprint Check")
736
print("10) Location OSINT")
737
print("11) AI Fake Follower Detection (advanced)")
738
print("12) Engagement & Reel Analytics")
739
print("13) Sentiment on Comments")
740
print("14) Deep Footprint (100+ platforms)")
741
print("15) Username Monitor")
742
print("16) Hashtag OSINT")
743
print("17) Comment Intelligence")
744
print("18) AI Risk Score (aggregate)")
745
print("19) Story Highlights (limited public)")
746
print("20) IP Log Checker (user-provided export)")
747
print("21) Username History")
748
print("22) Reel analytics")
749
print("23) Face similarity helper (local)")
750
print("24) Raw HTML (dark/raw view)")
751
print("0) Exit" + W)
752
ch = input(Y + "\nChoice > " + W).strip()
753
if ch=="0": print(G+"Bye - use ethically."+W); break
754
755
# each option prompts for username/params and runs function, shows result and offers to save.
756
if ch=="1":
757
usr = input("Instagram username > ").strip()
758
loading("Fetching profile")
759
res = fetch_profile_public(usr)
760
panel(f"Profile: {usr}", json.dumps(res, indent=2, ensure_ascii=False))
761
if input("Save (y/n)? > ").strip().lower()=="y": print("Saved:", save_report(f"profile_{usr}", res))
762
prompt_enter()
763
764
elif ch=="2":
765
usr = input("Username to scan > ").strip()
766
loading("Scanning platforms")
767
if aiohttp:
768
res = asyncio.run(username_global_scan(usr))
769
else:
770
# sync fallback
771
res={}
772
for k,t in PLATFORMS.items():
773
url=t.format(u=usr)
774
try:
775
r = requests.head(url, headers=HEADERS, allow_redirects=True, timeout=8)
776
status = r.status_code
777
except Exception:
778
status=None
779
res[k]={"url":url,"status":status}
780
panel(f"Username scan: {usr}", json.dumps(res,indent=2))
781
if input("Save (y/n)? > ").strip().lower()=="y": print("Saved:", save_report(f"uname_scan_{usr}", res))
782
prompt_enter()
783
784
elif ch=="3":
785
panel("IG Ban/Unban Forms", json.dumps(IG_FORMS, indent=2))
786
if input("Open links (y/n)? > ").strip().lower()=="y":
787
import webbrowser
788
for v in IG_FORMS.values(): webbrowser.open(v)
789
prompt_enter()
790
791
elif ch=="4":
792
usr = input("Username > ").strip()
793
loading("Estimating account age (instaloader recommended)")
794
res = account_age_estimate(usr)
795
panel("Account Age", json.dumps(res,indent=2))
796
if input("Save? > ").strip().lower()=="y": print("Saved:", save_report(f"age_{usr}",res))
797
prompt_enter()
798
799
elif ch=="5":
800
identifier = input("Email (for HIBP) > ").strip()
801
if not identifier:
802
print(R+"Email required"+W); prompt_enter(); continue
803
loading("Checking HIBP")
804
res = hibp_check(identifier)
805
panel("HIBP", json.dumps(res,indent=2))
806
if input("Save? > ").strip().lower()=="y": print("Saved:", save_report(f"hibp_{identifier}",res))
807
prompt_enter()
808
809
elif ch=="6":
810
usr=input("Username > ").strip()
811
sample = input("Sample size (default 200) > ").strip(); sample=int(sample) if sample.isdigit() else 200
812
loading("Detecting bot-like followers")
813
res = detect_bot_followers(usr, sample=sample)
814
panel("Bot detect", json.dumps(res,indent=2))
815
if input("Save? > ").strip().lower()=="y": print("Saved:", save_report(f"bot_{usr}",res))
816
prompt_enter()
817
818
elif ch=="7":
819
usr = input("Username > ").strip()
820
sf = input("sample followers (200) > ").strip(); sf=int(sf) if sf.isdigit() else 200
821
sfg = input("sample following (200) > ").strip(); sfg=int(sfg) if sfg.isdigit() else 200
822
loading("Building relation graph")
823
res = build_relation_graph(usr, sample_followers=sf, sample_following=sfg)
824
panel("Relation Graph", json.dumps(res,indent=2))
825
prompt_enter()
826
827
elif ch=="8":
828
usr=input("Username > ").strip()
829
loading("Running shadowban heuristic")
830
res = shadowban_heuristic(usr)
831
panel("Shadowban", json.dumps(res,indent=2))
832
if input("Save? > ").strip().lower()=="y": print("Saved:", save_report(f"shadow_{usr}",res))
833
prompt_enter()
834
835
elif ch=="9":
836
usr=input("Username > ").strip()
837
loading("Analyzing posting hours & UA hints")
838
res = device_fingerprint_analysis(usr)
839
panel("Device fingerprint", json.dumps(res,indent=2))
840
prompt_enter()
841
842
elif ch=="10":
843
usr=input("Username > ").strip()
844
loading("Extracting location tags")
845
res=extract_locations(usr)
846
panel("Locations", json.dumps(res,indent=2))
847
prompt_enter()
848
849
elif ch=="11":
850
usr=input("Username > ").strip()
851
use_ml = input("Use ML clustering (if sklearn installed)? (y/n) > ").strip().lower().startswith("y")
852
loading("Running fake follower detector")
853
res = fake_follower_detector(usr, sample=500, use_ml=use_ml)
854
panel("Fake follower detect", json.dumps(res,indent=2))
855
prompt_enter()
856
857
elif ch in ["12","22"]:
858
usr=input("Username > ").strip()
859
loading("Collecting engagement & reels")
860
res = engagement_and_reels(usr)
861
panel("Engagement & Reels", json.dumps(res,indent=2))
862
prompt_enter()
863
864
elif ch=="13":
865
usr=input("Username > ").strip()
866
loading("Analyzing comment sentiment")
867
res = comment_sentiment(usr)
868
panel("Comment Sentiment", json.dumps(res,indent=2))
869
prompt_enter()
870
871
elif ch=="14":
872
usr=input("Username > ").strip()
873
loading("Running deep footprint")
874
res = deep_footprint(usr)
875
panel("Deep Footprint", json.dumps(res,indent=2))
876
prompt_enter()
877
878
elif ch=="15":
879
usr=input("Username > ").strip()
880
interval = input("Poll every N seconds (default 60) > ").strip(); interval=int(interval) if interval.isdigit() else 60
881
duration = input("Duration seconds (default 600) > ").strip(); duration=int(duration) if duration.isdigit() else 600
882
outfile = f"watch_{usr}.json"
883
loading(f"Monitoring username for {duration}s")
884
hist=watch_username(usr, interval=interval, duration=duration, output_file=outfile)
885
panel("Monitor done", f"Saved to {outfile}")
886
prompt_enter()
887
888
elif ch=="16":
889
tag=input("Hashtag (without #) > ").strip()
890
loading("Checking hashtag")
891
res=hashtag_osint(tag)
892
panel("Hashtag OSINT", json.dumps(res,indent=2))
893
prompt_enter()
894
895
elif ch=="17":
896
usr=input("Username > ").strip()
897
loading("Collecting comment intelligence")
898
res=comment_intel(usr)
899
panel("Comment Intel", json.dumps(res,indent=2))
900
prompt_enter()
901
902
elif ch=="18":
903
usr=input("Username > ").strip()
904
# we'll compute aggregated signals
905
loading("Computing risk score (may take time)")
906
p = fetch_profile_public(usr)
907
b = detect_bot_followers(usr, sample=300) if instaloader else {}
908
s = shadowban_heuristic(usr) if instaloader else {}
909
hb = None
910
if HIBP_KEY:
911
# no email - skip; user may need to supply email
912
hb = None
913
sent = comment_sentiment(usr) if (instaloader and (VADER or TEXTBLOB)) else None
914
res = ai_risk_score(p,b,s, breach_res=hb, sentiment_res=sent)
915
panel("AI Risk Score", json.dumps(res,indent=2))
916
prompt_enter()
917
918
elif ch=="19":
919
usr=input("Username > ").strip()
920
loading("Fetching highlights (best-effort)")
921
res = story_highlights(usr)
922
panel("Story Highlights (public-limited)", json.dumps(res,indent=2))
923
prompt_enter()
924
925
elif ch=="20":
926
path=input("Path to IG export JSON (user-provided only) > ").strip()
927
res = ip_log_checker_from_export(path)
928
panel("IP Log Check", json.dumps(res,indent=2))
929
prompt_enter()
930
931
elif ch=="21":
932
usr=input("Username > ").strip()
933
loading("Querying Wayback CDX (limited)")
934
res = username_history_wayback(usr)
935
panel("Wayback Captures", json.dumps(res,indent=2))
936
prompt_enter()
937
938
elif ch=="23":
939
path=input("Local image path > ").strip()
940
res = face_similarity_instructions(path)
941
panel("Image pHash", json.dumps(res,indent=2))
942
prompt_enter()
943
944
elif ch=="24":
945
usr=input("Username > ").strip()
946
loading("Fetching raw HTML snippet")
947
res=raw_html_fetch(usr)
948
panel("Raw HTML (snippet)", json.dumps(res,indent=2))
949
prompt_enter()
950
951
else:
952
print(R+"Invalid choice"+W); prompt_enter()
953
954
if __name__=="__main__":
955
try:
956
main_menu()
957
except KeyboardInterrupt:
958
print("\nExiting...")
959
960