Path: blob/Hacker-x-Phantom/X-IG-OSINT.py
1 views
unlisted
#!/usr/bin/env python31# x_ig_OSINT.py2# X IG OSINT — BEAST MODE (public-only, legal)3# Features: 1..24 (advanced, see README at top)4# Author: generated for user. Use ethically.56import os, sys, re, json, time, asyncio, math, hashlib7from datetime import datetime8from urllib.parse import quote_plus9import requests1011# Optional imports (graceful)12try:13import instaloader14except Exception:15instaloader = None1617try:18import aiohttp, async_timeout19except Exception:20aiohttp = None21async_timeout = None2223try:24from vaderSentiment.vaderSentiment import SentimentIntensityAnalyzer25VADER = True26except Exception:27VADER = False2829try:30from textblob import TextBlob31TEXTBLOB = True32except Exception:33TEXTBLOB = False3435try:36import numpy as np37except Exception:38np = None3940try:41import matplotlib.pyplot as plt42except Exception:43plt = None4445try:46import networkx as nx47import community as community_louvain48except Exception:49nx = None50community_louvain = None5152# small ML optional packages53try:54from sklearn.cluster import KMeans55SKLEARN = True56except Exception:57SKLEARN = False5859# rich for UI60try:61from rich.console import Console62from rich.panel import Panel63from rich.table import Table64from rich.progress import Progress, SpinnerColumn, BarColumn, TextColumn65console = Console()66RICH = True67except Exception:68RICH = False6970# colors fallback71P = "\033[95m"; G = "\033[92m"; C = "\033[96m"; Y="\033[93m"; R="\033[91m"; W="\033[0m"7273HEADERS = {"User-Agent":"X-IG-OSINT-HACKER X PHANTOM/1.0 (Public OSINT)"}74REPORT_DIR = "beast_reports"75os.makedirs(REPORT_DIR, exist_ok=True)76HIBP_KEY = os.environ.get("HIBP_KEY", None)7778ASCII = r"""79$$\ $$\ $$$$$$\ $$$$$$\ $$$$$$\ $$$$$$\ $$$$$$\ $$\ $$\ $$$$$$$$\80$$ | $$ | \_$$ _|$$ __$$\ $$ __$$\ $$ __$$\ \_$$ _|$$$\ $$ |\__$$ __|81\$$\ $$ | $$ | $$ / \__| $$ / $$ |$$ / \__| $$ | $$$$\ $$ | $$ |82\$$$$ /$$$$$$\ $$ | $$ |$$$$\ $$ | $$ |\$$$$$$\ $$ | $$ $$\$$ | $$ |83$$ $$< \______|$$ | $$ |\_$$ | $$ | $$ | \____$$\ $$ | $$ \$$$$ | $$ |84$$ /\$$\ $$ | $$ | $$ | $$ | $$ |$$\ $$ | $$ | $$ |\$$$ | $$ |85$$ / $$ | $$$$$$\ \$$$$$$ | $$$$$$ |\$$$$$$ |$$$$$$\ $$ | \$$ | $$ |86\__| \__| \______| \______/ \______/ \______/ \______|\__| \__| \__|87X IG OSINT — BEAST MODE (public-only) Hacker X Phantom88"""8990def clear(): os.system("clear" if os.name!='nt' else "cls")91def panel(title, content):92if RICH:93console.print(Panel(str(content), title=f"[magenta]{title}[/magenta]"))94else:95print(f"\n=== {title} ===\n{content}\n")9697def loading(text="Working", steps=24, delay=0.02):98if RICH:99with Progress(SpinnerColumn(), TextColumn(f"[green]{text}..."), BarColumn()) as p:100task = p.add_task("", total=steps)101for _ in range(steps):102p.advance(task); time.sleep(delay)103else:104print(P + text + "..." + W, end="", flush=True)105for _ in range(steps):106print(G + "▮" + W, end="", flush=True); time.sleep(delay)107print()108109# ---------- Basic helpers ----------110def save_report(prefix, data):111ts = datetime.utcnow().strftime("%Y%m%dT%H%M%SZ")112fn = os.path.join(REPORT_DIR, f"{prefix}_{ts}.json")113with open(fn,"w",encoding="utf-8") as f: json.dump(data,f,indent=2,ensure_ascii=False)114return fn115116def safe_get(url, headers=HEADERS, timeout=12):117try:118r = requests.get(url, headers=headers, timeout=timeout)119return r120except Exception as e:121return None122123# ---------- Core: Profile Info (real public) ----------124def fetch_profile_public(username):125"""Use instaloader if present, else HTML parse. Returns dict."""126out = {"username":username, "fetched":False, "timestamp":datetime.utcnow().isoformat()}127if instaloader:128try:129L = instaloader.Instaloader(download_pictures=False, download_videos=False, save_metadata=False)130profile = instaloader.Profile.from_username(L.context, username)131out.update({132"full_name": profile.full_name,133"bio": profile.biography,134"external_url": profile.external_url,135"followers": profile.followers,136"following": profile.followees,137"media_count": profile.mediacount,138"is_private": profile.is_private,139"is_verified": profile.is_verified,140"profile_pic_url": profile.profile_pic_url,141})142out["fetched"] = True143return out144except Exception as e:145out["error_instaloader"] = str(e)146# Fallback parse147url = f"https://www.instagram.com/{username}/"148r = safe_get(url)149if not r:150out["error_http"] = "no_response"151return out152out["http_status"] = r.status_code153if r.status_code != 200:154out["error_http"] = f"status_{r.status_code}"155return out156html = r.text157m = re.search(r"window\._sharedData\s*=\s*({.*?});</script>", html, flags=re.S)158if m:159try:160data = json.loads(m.group(1))161user = data["entry_data"]["ProfilePage"][0]["graphql"]["user"]162out.update({163"full_name": user.get("full_name"),164"bio": user.get("biography"),165"followers": user.get("edge_followed_by",{}).get("count"),166"following": user.get("edge_follow",{}).get("count"),167"media_count": user.get("edge_owner_to_timeline_media",{}).get("count"),168"is_private": user.get("is_private"),169"is_verified": user.get("is_verified"),170"profile_pic_url": user.get("profile_pic_url_hd") or user.get("profile_pic_url"),171})172out["fetched"]=True173except Exception as e:174out["error_parse"]=str(e)175# try meta tags176if not out.get("fetched"):177mt = re.search(r'<meta property="og:title" content="([^"]+)"', html)178md = re.search(r'<meta property="og:description" content="([^"]+)"', html)179if mt: out["full_name"] = mt.group(1)180if md: out["bio"] = md.group(1)181out["fetched"] = True182return out183184# ---------- 2: Username global scanner (async) ----------185PLATFORMS = {186"github":"https://github.com/{u}",187"x":"https://x.com/{u}",188"instagram":"https://www.instagram.com/{u}/",189"facebook":"https://www.facebook.com/{u}",190"linkedin":"https://www.linkedin.com/in/{u}",191"youtube":"https://www.youtube.com/{u}",192"tiktok":"https://www.tiktok.com/@{u}",193"reddit":"https://www.reddit.com/user/{u}",194"telegram":"https://t.me/{u}",195"snapchat":"https://www.snapchat.com/add/{u}",196"pinterest":"https://www.pinterest.com/{u}"197}198199async def _head(session, url):200try:201with async_timeout.timeout(8):202async with session.head(url, allow_redirects=True) as resp:203return resp.status204except Exception:205try:206with async_timeout.timeout(8):207async with session.get(url, allow_redirects=True) as resp:208return resp.status209except Exception:210return None211212async def username_global_scan(username):213if aiohttp is None:214return {"error":"aiohttp_missing"}215results = {}216async with aiohttp.ClientSession(headers=HEADERS) as session:217tasks=[]218for k,t in PLATFORMS.items():219url = t.format(u=username)220tasks.append(asyncio.create_task(_head(session,url)))221results[k] = {"url":url}222codes = await asyncio.gather(*tasks)223i=0224for k in PLATFORMS.keys():225results[k]["status"] = codes[i]226i+=1227return results228229# ---------- 3: IG Ban/Unban (official links) ----------230IG_FORMS = {231"disabled_account":"https://help.instagram.com/366993040048856",232"appeal_disabled":"https://www.facebook.com/help/contact/606967319425038",233"hacked":"https://help.instagram.com/368191326593075",234"impersonation":"https://help.instagram.com/contact/636276399721841"235}236237# ---------- 4: Account age (first post via instaloader) ----------238def account_age_estimate(username, max_posts=1000):239if not instaloader:240return {"error":"instaloader_missing"}241try:242L = instaloader.Instaloader(download_pictures=False, download_videos=False, save_metadata=False)243profile = instaloader.Profile.from_username(L.context, username)244if profile.is_private:245return {"error":"profile_private"}246earliest=None; cnt=0247for post in profile.get_posts():248dt = post.date_utc249if earliest is None or dt < earliest: earliest = dt250cnt+=1251if cnt>=max_posts: break252if earliest:253days = (datetime.utcnow()-earliest).days254return {"earliest_post":earliest.isoformat(), "age_days":days, "age_years":round(days/365.0,2)}255return {"error":"no_posts"}256except Exception as e:257return {"error":str(e)}258259# ---------- 5: Data breach (HaveIBeenPwned) ----------260def hibp_check(email):261if not HIBP_KEY:262return {"error":"hibp_key_missing"}263try:264headers = {"hibp-api-key":HIBP_KEY, "user-agent":"X-IG-OSINT"}265url = f"https://haveibeenpwned.com/api/v3/breachedaccount/{quote_plus(email)}"266r = requests.get(url, headers=headers, timeout=12)267if r.status_code==200: return {"breaches":r.json()}268elif r.status_code==404: return {"breaches":[]}269else: return {"status":r.status_code}270except Exception as e:271return {"error":str(e)}272273# ---------- 6: AI-based Fake Follower Detection (heuristic + optional ML) ----------274def fake_follower_detector(username, sample=500, use_ml=False):275if not instaloader:276return {"error":"instaloader_missing"}277try:278L = instaloader.Instaloader()279profile = instaloader.Profile.from_username(L.context, username)280if profile.is_private: return {"error":"profile_private"}281bots=0; total=0; suspicious=[]282features = []283for f in profile.get_followers():284uname = f.username285total += 1286digits = sum(c.isdigit() for c in uname)287score = 0288if digits>=3: score+=1289if len(uname)<=4: score+=1290if f.is_verified: score-=1291# heuristic: no biography and no posts -> suspicious292try:293bio = f.biography if hasattr(f,'biography') else None294mcount = f.mediacount if hasattr(f,'mediacount') else 0295except Exception:296bio=None; mcount=0297if not bio or bio.strip()=="":298score+=1299if mcount==0: score+=1300if score>=2:301bots+=1; suspicious.append(uname)302if use_ml and SKLEARN:303features.append([digits, 1 if mcount==0 else 0, len(uname)])304if total>=sample: break305ratio = round(bots/total,4) if total else 0306res = {"sampled":total,"bot_like":bots,"bot_ratio":ratio,"suspicious_samples":suspicious[:100]}307if use_ml and SKLEARN and features:308# a simple KMeans cluster to separate 2 clusters; label cluster with higher mean suspiciousness as bots309try:310km = KMeans(n_clusters=2, random_state=0).fit(features)311res["ml_clusters"] = True312except Exception as e:313res["ml_error"]=str(e)314return res315except Exception as e:316return {"error":str(e)}317318# ---------- 7: Relationship Mapping (graph + community detection) ----------319def build_relation_graph(username, sample_followers=300, sample_following=300, probe_each=30):320if not instaloader:321return {"error":"instaloader_missing"}322try:323if nx is None:324# produce JSON adjacency only325L=instaloader.Instaloader()326profile=instaloader.Profile.from_username(L.context, username)327followers=[]; following=[]328cnt=0329for f in profile.get_followers():330followers.append(f.username); cnt+=1331if cnt>=sample_followers: break332cnt=0333for f in profile.get_followees():334following.append(f.username); cnt+=1335if cnt>=sample_following: break336mutuals = list(set(followers).intersection(set(following)))337return {"followers_sampled":len(followers),"following_sampled":len(following),"mutuals":mutuals}338L=instaloader.Instaloader(); profile = instaloader.Profile.from_username(L.context, username)339if profile.is_private: return {"error":"profile_private"}340Gs = nx.Graph()341Gs.add_node(username, type='target')342followers=[]; following=[]343cnt=0344for f in profile.get_followers():345followers.append(f.username); Gs.add_node(f.username, type='follower'); Gs.add_edge(username,f.username); cnt+=1346if cnt>=sample_followers: break347cnt=0348for f in profile.get_followees():349following.append(f.username); Gs.add_node(f.username, type='following'); Gs.add_edge(username,f.username); cnt+=1350if cnt>=sample_following: break351# probe few followers' followees to find shared connections352for uname in followers[:probe_each]:353try:354p = instaloader.Profile.from_username(L.context, uname)355c=0356for ff in p.get_followees():357Gs.add_node(ff.username); Gs.add_edge(uname, ff.username)358c+=1359if c>=30: break360except Exception:361continue362# community detection363partition = community_louvain.best_partition(Gs) if community_louvain else {}364# export as adjacency365adj = {n: list(Gs.neighbors(n)) for n in Gs.nodes()}366fname = save_report(f"relation_graph_{username}", {"nodes":list(Gs.nodes()), "edges":list(Gs.edges()), "partition":partition})367return {"saved_graph":fname, "nodes_count":Gs.number_of_nodes(), "edges_count":Gs.number_of_edges(), "partition_sample": dict(list(partition.items())[:30])}368except Exception as e:369return {"error":str(e)}370371# ---------- 8: Shadowban Detector (improved heuristic) ----------372def shadowban_heuristic(username, recent_posts=5, hashtags_per_post=5):373if not instaloader:374return {"error":"instaloader_missing"}375try:376L=instaloader.Instaloader()377profile=instaloader.Profile.from_username(L.context, username)378if profile.is_private: return {"error":"profile_private"}379posts=[]380for p in profile.get_posts():381posts.append(p)382if len(posts)>=recent_posts: break383if not posts: return {"error":"no_posts"}384results=[]385for p in posts:386code = p.shortcode387tags = list(p.caption_hashtags) if hasattr(p,'caption_hashtags') else []388tags = tags[:hashtags_per_post]389tag_presence = {}390for tag in tags:391url=f"https://www.instagram.com/explore/tags/{tag}/"392r = safe_get(url)393if not r: tag_presence[tag]=None394else:395tag_presence[tag] = (code in r.text)396results.append({"shortcode":code,"tag_presence":tag_presence})397negative = sum(1 for r in results for v in r["tag_presence"].values() if v is False)398total = sum(len(r["tag_presence"]) for r in results)399score = (negative/total) if total else 0400verdict = "No strong shadowban signs"401if score>0.6: verdict="Strong shadowban signals"402elif score>0.25: verdict="Some shadowban signals"403return {"results":results,"negative":negative,"total":total,"score":score,"verdict":verdict}404except Exception as e:405return {"error":str(e)}406407# ---------- 9: Device fingerprinting (posting hours + UA heuristics) ----------408def device_fingerprint_analysis(username, sample_posts=200):409if not instaloader:410return {"error":"instaloader_missing"}411try:412L=instaloader.Instaloader()413profile=instaloader.Profile.from_username(L.context, username)414if profile.is_private: return {"error":"profile_private"}415hours = {}416ua_counts = {}417count=0418for p in profile.get_posts():419dt = p.date_utc420hours[dt.hour]=hours.get(dt.hour,0)+1421# instaloader doesn't always expose user agent. Try _node:422node = getattr(p,"_node",{})423ua = node.get("device","unknown") or node.get("user_agent","unknown")424ua_counts[ua]=ua_counts.get(ua,0)+1425count+=1426if count>=sample_posts: break427return {"post_hour_distribution":hours,"ua_counts":ua_counts}428except Exception as e:429return {"error":str(e)}430431# ---------- 10: Location OSINT ----------432def extract_locations(username, sample_posts=500):433if not instaloader:434return {"error":"instaloader_missing"}435try:436L=instaloader.Instaloader(); profile=instaloader.Profile.from_username(L.context, username)437if profile.is_private: return {"error":"profile_private"}438places={}439cnt=0440for p in profile.get_posts():441loc = getattr(p,"location",None)442if loc:443name = getattr(loc,"name",None) or getattr(loc,"slug",None)444if name: places[name]=places.get(name,0)+1445cnt+=1446if cnt>=sample_posts: break447return {"sampled_posts":cnt,"places":places}448except Exception as e:449return {"error":str(e)}450451# ---------- 11: Engagement Rate & Reel analytics ----------452def engagement_and_reels(username, sample_posts=100):453if not instaloader:454return {"error":"instaloader_missing"}455try:456L=instaloader.Instaloader()457profile=instaloader.Profile.from_username(L.context, username)458if profile.is_private: return {"error":"profile_private"}459likes=[]; comments=[]460reels=[]461cnt=0462for p in profile.get_posts():463# p.likes p.comments may be present464try:465likes.append(p.likes)466comments.append(p.comments)467if p.is_video:468reels.append({"shortcode":p.shortcode,"likes":p.likes,"comments":p.comments,"views": getattr(p,"video_view_count",None)})469except Exception:470pass471cnt+=1472if cnt>=sample_posts: break473avg_likes = sum(likes)/len(likes) if likes else 0474avg_comments = sum(comments)/len(comments) if comments else 0475follower_count = profile.followers476engagement_rate = (avg_likes+avg_comments) / follower_count * 100 if follower_count else 0477return {"avg_likes":avg_likes,"avg_comments":avg_comments,"engagement_rate_pct":round(engagement_rate,3),"reels_sample":reels[:30]}478except Exception as e:479return {"error":str(e)}480481# ---------- 12: Sentiment Analysis of comments (VADER/TextBlob) ----------482def comment_sentiment(username, sample_comments=500):483if not instaloader:484return {"error":"instaloader_missing"}485if not (VADER or TEXTBLOB):486return {"error":"no_sentiment_libs"}487try:488L=instaloader.Instaloader(); profile=instaloader.Profile.from_username(L.context, username)489if profile.is_private: return {"error":"profile_private"}490comments_texts=[]491cnt=0492for post in profile.get_posts():493for c in post.get_comments():494comments_texts.append(c.text)495cnt+=1496if cnt>=sample_comments: break497if cnt>=sample_comments: break498if not comments_texts: return {"error":"no_comments"}499vader = SentimentIntensityAnalyzer() if VADER else None500pos=neg=neu=0501for t in comments_texts:502if vader:503s=vader.polarity_scores(t)504if s["compound"]>=0.05: pos+=1505elif s["compound"]<=-0.05: neg+=1506else: neu+=1507else:508tb = TextBlob(t)509pol = tb.sentiment.polarity510if pol>0.05: pos+=1511elif pol<-0.05: neg+=1512else: neu+=1513total = pos+neg+neu514return {"comments_analyzed":total,"positive":pos,"negative":neg,"neutral":neu,"positive_pct":round(pos/total*100,2)}515except Exception as e:516return {"error":str(e)}517518# ---------- 13: Deep footprint (username search across many) ----------519def deep_footprint(username, extra_platforms=None):520all_platforms = dict(PLATFORMS)521if extra_platforms:522all_platforms.update(extra_platforms)523res = {}524for k,t in all_platforms.items():525url = t.format(u=username)526r = None527try:528r = requests.head(url, headers=HEADERS, allow_redirects=True, timeout=8)529status = r.status_code if r else None530except Exception:531status = None532res[k] = {"url":url, "status": status}533return res534535# ---------- 14: Username monitor (simple) ----------536def watch_username(username, interval=60, duration=3600, output_file=None):537"""Polls platforms for username availability for 'duration' seconds every 'interval' seconds. Saves to file if provided."""538end = time.time() + duration539history=[]540while time.time() < end:541row = {"ts":datetime.utcnow().isoformat(), "scan": deep_footprint(username)}542history.append(row)543if output_file:544with open(output_file, "w", encoding="utf-8") as f: json.dump(history, f, indent=2)545time.sleep(interval)546return history547548# ---------- 15: Reverse image helper (local only - prepare hashes & instruct) ----------549def image_similarity_helper(image_path):550"""Generates perceptual hash and instructs user to use reverse image engines; we won't call external paid APIs."""551try:552from PIL import Image553import imagehash554except Exception:555return {"error":"requires pillow & imagehash (pip install pillow imagehash)"}556try:557img = Image.open(image_path)558ph = str(imagehash.phash(img))559return {"phash":ph, "next_steps": "Use this pHash to search in local dataset or third-party reverse image engines (manual upload recommended)."}560except Exception as e:561return {"error":str(e)}562563# ---------- 16: Hashtag OSINT (quick heuristic) ----------564def hashtag_osint(tag):565tag = tag.lstrip("#")566urls = {567"instagram": f"https://www.instagram.com/explore/tags/{quote_plus(tag)}/",568"twitter_search": f"https://twitter.com/search?q={quote_plus('#'+tag)}",569"google_trends": f"https://trends.google.com/trends/explore?q=%23{quote_plus(tag)}"570}571# check visibility on IG572r = safe_get(urls["instagram"])573vis = {"status": r.status_code if r else None, "note":"heuristic"}574# toxic words heuristic575toxic_words = ["spam","scam","nsfw","bomb","terror"]576score = sum(1 for w in toxic_words if w in tag.lower())577return {"urls":urls,"ig_visibility":vis,"toxicity_score":score}578579# ---------- 17: Comment intelligence (keywords & spammy commenters) ----------580def comment_intel(username, sample_posts=50):581if not instaloader: return {"error":"instaloader_missing"}582try:583L=instaloader.Instaloader(); profile=instaloader.Profile.from_username(L.context, username)584comments=[]; commenters={}585cnt=0586for p in profile.get_posts():587for c in p.get_comments():588comments.append(c.text)589commenters[c.owner.username] = commenters.get(c.owner.username,0)+1590cnt+=1591if cnt>=sample_posts: break592# top commenters593top_commenters = sorted(commenters.items(), key=lambda x:-x[1])[:50]594# keyword freq595words={}596for t in comments:597for w in re.findall(r"\w{3,}", t.lower()):598words[w]=words.get(w,0)+1599top_words = sorted(words.items(), key=lambda x:-x[1])[:40]600return {"comments_sampled":len(comments),"top_commenters":top_commenters,"top_words":top_words}601except Exception as e:602return {"error":str(e)}603604# ---------- 18: AI Risk Score aggregator ----------605def ai_risk_score(profile_info_res, bot_detect_res, shadow_res, breach_res=None, sentiment_res=None):606score=0; reasons=[]607if profile_info_res.get("is_private"): score-=2; reasons.append("private_profile")608if profile_info_res.get("is_verified"): score-=5; reasons.append("verified")609# bot ratio610br = bot_detect_res.get("bot_ratio") if isinstance(bot_detect_res, dict) else None611if br is not None:612score += br*20613reasons.append(f"bot_ratio_{br}")614# shadowban615if isinstance(shadow_res, dict) and shadow_res.get("score",0)>0.3:616score += 30; reasons.append("shadow_signals")617# breach618if breach_res and isinstance(breach_res, dict) and breach_res.get("breaches"):619score += 40; reasons.append("breach_found")620# sentiment toxicity621if sentiment_res and isinstance(sentiment_res, dict) and sentiment_res.get("negative",0) > sentiment_res.get("positive",0):622score += 10; reasons.append("negative_comments")623final = min(100, max(0, int(score)))624verdict = "Low risk"625if final>75: verdict="High risk"626elif final>40: verdict="Medium risk"627return {"score":final,"verdict":verdict,"reasons":reasons}628629# ---------- 19: Story highlights (public) ----------630def story_highlights(username):631if not instaloader: return {"error":"instaloader_missing"}632try:633L=instaloader.Instaloader(); profile=instaloader.Profile.from_username(L.context, username)634if profile.is_private: return {"error":"profile_private"}635highlights = []636# instaloader cannot fetch highlights easily without login; this is best-effort (may be empty)637# We will attempt to fetch profile metadata for highlights field if present638# Fallback: return not available639return {"notes":"highlights extraction requires authenticated API or profile page parsing; limited in public mode"}640except Exception as e:641return {"error":str(e)}642643# ---------- 20: IP log checker (legal only) ----------644def ip_log_checker_from_export(export_file):645"""If user provides IG data export (JSON) with IPs, parse and show login IP timeline.646This function DOES NOT fetch private data — user must supply file."""647try:648with open(export_file,"r",encoding="utf-8") as f:649data=json.load(f)650# try known keys651ips=[]652def walk(d):653if isinstance(d, dict):654for k,v in d.items():655if "ip" in k.lower() and isinstance(v,str): ips.append(v)656walk(v)657elif isinstance(d,list):658for item in d: walk(item)659walk(data)660return {"found_ips": ips}661except Exception as e:662return {"error":str(e)}663664# ---------- 21: Username history (Wayback & caches - limited, we will query Wayback CDX) ----------665def username_history_wayback(username, limit=10):666# Query Wayback CDX for instagram.com/username pages667q = f"https://web.archive.org/cdx/search/cdx?url=www.instagram.com/{username}/*&output=json&limit={limit}"668r = safe_get(q)669if not r: return {"error":"no_response"}670try:671arr = r.json()672# arr[0] is header, rest are captures673captures = arr[1:] if isinstance(arr,list) and len(arr)>1 else []674return {"captures_count":len(captures),"captures_sample":captures}675except Exception as e:676return {"error":str(e)}677678# ---------- 22: Reel analytics (part of engagement function) ----------679# (use engagement_and_reels above)680681# ---------- 23: Facial similarity cross-match helper ----------682# (we provide pHash and instructions to use reverse image search; no automated cross-site scraping)683def face_similarity_instructions(image_path):684# compute pHash685try:686from PIL import Image687import imagehash688except Exception:689return {"error":"install pillow & imagehash (pip install pillow imagehash)"}690try:691img=Image.open(image_path)692ph = str(imagehash.phash(img))693return {"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."}694except Exception as e:695return {"error":str(e)}696697# ---------- 24: Raw HTML Fetch (Dark Mode raw viewer) ----------698def raw_html_fetch(username):699url = f"https://www.instagram.com/{username}/"700r = safe_get(url)701if not r: return {"error":"no_response"}702return {"status":r.status_code, "html_snippet": r.text[:5000]} # don't dump entire page to console703704# ---------- CLI and menu ----------705def header():706clear()707if RICH:708console.print(Panel(ASCII, title="[magenta]X IG OSINT — BEAST[/magenta]"))709else:710print(P + ASCII + W)711if not instaloader:712print(Y + "Note: instaloader not installed -> many features limited. Install: pip install instaloader" + W)713if not aiohttp:714print(Y + "Note: aiohttp missing -> fast username scanning limited. Install: pip install aiohttp async-timeout" + W)715if not (VADER or TEXTBLOB):716print(Y + "Note: sentiment libs missing -> pip install vaderSentiment textblob" + W)717print()718719def prompt_enter():720input(G + "\nPress ENTER to continue..." + W)721722def main_menu():723while True:724header()725print(G + "Choose feature set (public-only, legal):" + W)726print(C + "1) Profile Info")727print("2) Username Scanner")728print("3) IG Ban/Unban Forms")729print("4) Account Age")730print("5) Data Breach Lookup")731print("6) Bot Followers Detect")732print("7) Relationship Mapping")733print("8) Shadowban Detector")734print("9) Device Fingerprint Check")735print("10) Location OSINT")736print("11) AI Fake Follower Detection (advanced)")737print("12) Engagement & Reel Analytics")738print("13) Sentiment on Comments")739print("14) Deep Footprint (100+ platforms)")740print("15) Username Monitor")741print("16) Hashtag OSINT")742print("17) Comment Intelligence")743print("18) AI Risk Score (aggregate)")744print("19) Story Highlights (limited public)")745print("20) IP Log Checker (user-provided export)")746print("21) Username History")747print("22) Reel analytics")748print("23) Face similarity helper (local)")749print("24) Raw HTML (dark/raw view)")750print("0) Exit" + W)751ch = input(Y + "\nChoice > " + W).strip()752if ch=="0": print(G+"Bye - use ethically."+W); break753754# each option prompts for username/params and runs function, shows result and offers to save.755if ch=="1":756usr = input("Instagram username > ").strip()757loading("Fetching profile")758res = fetch_profile_public(usr)759panel(f"Profile: {usr}", json.dumps(res, indent=2, ensure_ascii=False))760if input("Save (y/n)? > ").strip().lower()=="y": print("Saved:", save_report(f"profile_{usr}", res))761prompt_enter()762763elif ch=="2":764usr = input("Username to scan > ").strip()765loading("Scanning platforms")766if aiohttp:767res = asyncio.run(username_global_scan(usr))768else:769# sync fallback770res={}771for k,t in PLATFORMS.items():772url=t.format(u=usr)773try:774r = requests.head(url, headers=HEADERS, allow_redirects=True, timeout=8)775status = r.status_code776except Exception:777status=None778res[k]={"url":url,"status":status}779panel(f"Username scan: {usr}", json.dumps(res,indent=2))780if input("Save (y/n)? > ").strip().lower()=="y": print("Saved:", save_report(f"uname_scan_{usr}", res))781prompt_enter()782783elif ch=="3":784panel("IG Ban/Unban Forms", json.dumps(IG_FORMS, indent=2))785if input("Open links (y/n)? > ").strip().lower()=="y":786import webbrowser787for v in IG_FORMS.values(): webbrowser.open(v)788prompt_enter()789790elif ch=="4":791usr = input("Username > ").strip()792loading("Estimating account age (instaloader recommended)")793res = account_age_estimate(usr)794panel("Account Age", json.dumps(res,indent=2))795if input("Save? > ").strip().lower()=="y": print("Saved:", save_report(f"age_{usr}",res))796prompt_enter()797798elif ch=="5":799identifier = input("Email (for HIBP) > ").strip()800if not identifier:801print(R+"Email required"+W); prompt_enter(); continue802loading("Checking HIBP")803res = hibp_check(identifier)804panel("HIBP", json.dumps(res,indent=2))805if input("Save? > ").strip().lower()=="y": print("Saved:", save_report(f"hibp_{identifier}",res))806prompt_enter()807808elif ch=="6":809usr=input("Username > ").strip()810sample = input("Sample size (default 200) > ").strip(); sample=int(sample) if sample.isdigit() else 200811loading("Detecting bot-like followers")812res = detect_bot_followers(usr, sample=sample)813panel("Bot detect", json.dumps(res,indent=2))814if input("Save? > ").strip().lower()=="y": print("Saved:", save_report(f"bot_{usr}",res))815prompt_enter()816817elif ch=="7":818usr = input("Username > ").strip()819sf = input("sample followers (200) > ").strip(); sf=int(sf) if sf.isdigit() else 200820sfg = input("sample following (200) > ").strip(); sfg=int(sfg) if sfg.isdigit() else 200821loading("Building relation graph")822res = build_relation_graph(usr, sample_followers=sf, sample_following=sfg)823panel("Relation Graph", json.dumps(res,indent=2))824prompt_enter()825826elif ch=="8":827usr=input("Username > ").strip()828loading("Running shadowban heuristic")829res = shadowban_heuristic(usr)830panel("Shadowban", json.dumps(res,indent=2))831if input("Save? > ").strip().lower()=="y": print("Saved:", save_report(f"shadow_{usr}",res))832prompt_enter()833834elif ch=="9":835usr=input("Username > ").strip()836loading("Analyzing posting hours & UA hints")837res = device_fingerprint_analysis(usr)838panel("Device fingerprint", json.dumps(res,indent=2))839prompt_enter()840841elif ch=="10":842usr=input("Username > ").strip()843loading("Extracting location tags")844res=extract_locations(usr)845panel("Locations", json.dumps(res,indent=2))846prompt_enter()847848elif ch=="11":849usr=input("Username > ").strip()850use_ml = input("Use ML clustering (if sklearn installed)? (y/n) > ").strip().lower().startswith("y")851loading("Running fake follower detector")852res = fake_follower_detector(usr, sample=500, use_ml=use_ml)853panel("Fake follower detect", json.dumps(res,indent=2))854prompt_enter()855856elif ch in ["12","22"]:857usr=input("Username > ").strip()858loading("Collecting engagement & reels")859res = engagement_and_reels(usr)860panel("Engagement & Reels", json.dumps(res,indent=2))861prompt_enter()862863elif ch=="13":864usr=input("Username > ").strip()865loading("Analyzing comment sentiment")866res = comment_sentiment(usr)867panel("Comment Sentiment", json.dumps(res,indent=2))868prompt_enter()869870elif ch=="14":871usr=input("Username > ").strip()872loading("Running deep footprint")873res = deep_footprint(usr)874panel("Deep Footprint", json.dumps(res,indent=2))875prompt_enter()876877elif ch=="15":878usr=input("Username > ").strip()879interval = input("Poll every N seconds (default 60) > ").strip(); interval=int(interval) if interval.isdigit() else 60880duration = input("Duration seconds (default 600) > ").strip(); duration=int(duration) if duration.isdigit() else 600881outfile = f"watch_{usr}.json"882loading(f"Monitoring username for {duration}s")883hist=watch_username(usr, interval=interval, duration=duration, output_file=outfile)884panel("Monitor done", f"Saved to {outfile}")885prompt_enter()886887elif ch=="16":888tag=input("Hashtag (without #) > ").strip()889loading("Checking hashtag")890res=hashtag_osint(tag)891panel("Hashtag OSINT", json.dumps(res,indent=2))892prompt_enter()893894elif ch=="17":895usr=input("Username > ").strip()896loading("Collecting comment intelligence")897res=comment_intel(usr)898panel("Comment Intel", json.dumps(res,indent=2))899prompt_enter()900901elif ch=="18":902usr=input("Username > ").strip()903# we'll compute aggregated signals904loading("Computing risk score (may take time)")905p = fetch_profile_public(usr)906b = detect_bot_followers(usr, sample=300) if instaloader else {}907s = shadowban_heuristic(usr) if instaloader else {}908hb = None909if HIBP_KEY:910# no email - skip; user may need to supply email911hb = None912sent = comment_sentiment(usr) if (instaloader and (VADER or TEXTBLOB)) else None913res = ai_risk_score(p,b,s, breach_res=hb, sentiment_res=sent)914panel("AI Risk Score", json.dumps(res,indent=2))915prompt_enter()916917elif ch=="19":918usr=input("Username > ").strip()919loading("Fetching highlights (best-effort)")920res = story_highlights(usr)921panel("Story Highlights (public-limited)", json.dumps(res,indent=2))922prompt_enter()923924elif ch=="20":925path=input("Path to IG export JSON (user-provided only) > ").strip()926res = ip_log_checker_from_export(path)927panel("IP Log Check", json.dumps(res,indent=2))928prompt_enter()929930elif ch=="21":931usr=input("Username > ").strip()932loading("Querying Wayback CDX (limited)")933res = username_history_wayback(usr)934panel("Wayback Captures", json.dumps(res,indent=2))935prompt_enter()936937elif ch=="23":938path=input("Local image path > ").strip()939res = face_similarity_instructions(path)940panel("Image pHash", json.dumps(res,indent=2))941prompt_enter()942943elif ch=="24":944usr=input("Username > ").strip()945loading("Fetching raw HTML snippet")946res=raw_html_fetch(usr)947panel("Raw HTML (snippet)", json.dumps(res,indent=2))948prompt_enter()949950else:951print(R+"Invalid choice"+W); prompt_enter()952953if __name__=="__main__":954try:955main_menu()956except KeyboardInterrupt:957print("\nExiting...")958959960