octrouble 2.0.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.
@@ -0,0 +1,16 @@
1
+ Metadata-Version: 2.4
2
+ Name: octrouble
3
+ Version: 2.0.0
4
+ Summary: OSINT publik: URL, rekening, telepon, entropi, publikasi, institusi Indonesia, Blockchain
5
+ Requires-Python: >=3.8
6
+ Description-Content-Type: text/markdown
7
+ Requires-Dist: requests
8
+
9
+ # Octrouble
10
+ OSINT publik berbasis terminal.
11
+
12
+ ## Install
13
+ pip install octrouble
14
+
15
+ ## Jalankan
16
+ octrouble
@@ -0,0 +1,8 @@
1
+ # Octrouble
2
+ OSINT publik berbasis terminal.
3
+
4
+ ## Install
5
+ pip install octrouble
6
+
7
+ ## Jalankan
8
+ octrouble
File without changes
@@ -0,0 +1,539 @@
1
+ import sys, os, math, hashlib, re
2
+ from collections import Counter
3
+ from urllib.parse import quote, urlparse
4
+
5
+ try:
6
+ import requests
7
+ except ImportError:
8
+ print("Jalankan: pip install requests")
9
+ sys.exit(1)
10
+
11
+ HDR = {"User-Agent": "Octrouble/2.0", "Accept": "application/json"}
12
+
13
+ def get(url):
14
+ try:
15
+ return requests.get(url, headers=HDR, timeout=20, allow_redirects=True)
16
+ except:
17
+ return None
18
+
19
+ def enc(s):
20
+ return quote(s, safe="")
21
+
22
+ def feat_url():
23
+ u = input("URL: ").strip()
24
+ if not u: return
25
+ if "://" not in u: u = "https://" + u
26
+ https = u.startswith("https://")
27
+ host = urlparse(u).hostname or ""
28
+ ip_host = bool(re.match(r"^\d+\.\d+\.\d+\.\d+$", host))
29
+ puny = "xn--" in host
30
+ r = get(u)
31
+ code = r.status_code if r else "timeout"
32
+ print(f"\nHost : {host}")
33
+ print(f"HTTP code : {code}")
34
+ print(f"Skema : {'HTTPS' if https else 'HTTP (tidak terenkripsi)'}")
35
+ parts = host.split(".")
36
+ for n in (2, 3):
37
+ dom = ".".join(parts[-n:]) if len(parts) >= n else host
38
+ rd = get(f"https://rdap.org/domain/{dom}")
39
+ if rd and rd.status_code == 200:
40
+ try:
41
+ for ev in rd.json().get("events", []):
42
+ print(f" RDAP {ev.get('eventAction','?')} : {ev.get('eventDate','?')}")
43
+ except: pass
44
+ break
45
+ print(f"HTTPS : {'ya' if https else 'TIDAK'}")
46
+ print(f"IP host : {'ya' if ip_host else 'tidak'}")
47
+ print(f"Punycode : {'ya' if puny else 'tidak'}")
48
+ print(f"Panjang : {len(host)}")
49
+ if r and r.history:
50
+ print(f"Redirect : {' -> '.join(str(x.status_code) for x in r.history)} -> {r.status_code}")
51
+
52
+ def feat_account():
53
+ banks = [
54
+ ("BCA",[10]),("BNI",[10]),("BRI",[15]),("Mandiri",[13]),
55
+ ("BTN",[16]),("CIMB Niaga",[13]),("Permata",[16]),
56
+ ("Danamon",[10,16]),("BSI",[10]),("Maybank",[10,13]),
57
+ ("OCBC",[10,16]),("Panin",[10]),
58
+ ]
59
+ print("\nDaftar bank:")
60
+ for i,(n,ls) in enumerate(banks,1):
61
+ print(f" {i:2}. {n} ({'/'.join(map(str,ls))} digit)")
62
+ print(" 0. Auto")
63
+ pick = input("Pilih bank: ").strip()
64
+ raw = input("Nomor rekening: ").strip()
65
+ digits = "".join(c for c in raw if c.isdigit())
66
+ only_d = all(c.isdigit() or c in " -" for c in raw)
67
+ print(f"\nJumlah digit : {len(digits)}")
68
+ print(f"Hanya angka : {'ya' if only_d else 'tidak'}")
69
+ try: idx = int(pick)
70
+ except: idx = -1
71
+ if 1 <= idx <= len(banks):
72
+ name, lengths = banks[idx-1]
73
+ print(f"Bank : {name}")
74
+ print(f"Format sesuai : {'ya' if len(digits) in lengths else 'TIDAK'}")
75
+ else:
76
+ cands = [n for n,ls in banks if len(digits) in ls]
77
+ print(f"Kandidat : {', '.join(cands) if cands else '(tidak ada yang cocok)'}")
78
+
79
+ PREFIX_MAP = {
80
+ "0811":"Telkomsel","0812":"Telkomsel","0813":"Telkomsel",
81
+ "0821":"Telkomsel","0822":"Telkomsel","0823":"Telkomsel",
82
+ "0851":"Telkomsel","0852":"Telkomsel","0853":"Telkomsel",
83
+ "0814":"Indosat","0815":"Indosat","0816":"Indosat",
84
+ "0855":"Indosat","0856":"Indosat","0857":"Indosat","0858":"Indosat",
85
+ "0817":"XL","0818":"XL","0819":"XL","0859":"XL","0877":"XL","0878":"XL",
86
+ "0895":"Tri","0896":"Tri","0897":"Tri","0898":"Tri","0899":"Tri",
87
+ "0831":"Axis","0832":"Axis","0833":"Axis","0838":"Axis",
88
+ "0881":"Smartfren","0882":"Smartfren","0883":"Smartfren","0884":"Smartfren",
89
+ "0885":"Smartfren","0886":"Smartfren","0887":"Smartfren","0888":"Smartfren","0889":"Smartfren",
90
+ }
91
+ CC_MAP = [
92
+ ("62","Indonesia"),("60","Malaysia"),("65","Singapura"),("1","AS/Kanada"),
93
+ ("44","Inggris"),("61","Australia"),("91","India"),("86","Tiongkok"),
94
+ ("81","Jepang"),("82","Korea Selatan"),("49","Jerman"),("33","Prancis"),
95
+ ("7","Rusia"),("966","Arab Saudi"),("971","UEA"),("63","Filipina"),
96
+ ("66","Thailand"),("84","Vietnam"),("92","Pakistan"),("90","Turki"),
97
+ ]
98
+
99
+ def feat_phone():
100
+ raw = input("Nomor telepon: ").strip()
101
+ if not raw: return
102
+ plus = raw.startswith("+")
103
+ s = "".join(c for c in raw if c.isdigit())
104
+ code, country, nat, e164 = "", "(tidak diketahui)", "", ""
105
+ def match_cc(d):
106
+ for l in (3,2,1):
107
+ pre = d[:l]
108
+ for c,n in CC_MAP:
109
+ if c == pre: return c,n
110
+ return "",""
111
+ if plus:
112
+ code,country = match_cc(s)
113
+ if code == "62": nat="0"+s[2:]; e164="+62"+s[2:]
114
+ else: e164="+"+s
115
+ elif s.startswith("62"):
116
+ code,country="62","Indonesia"; nat="0"+s[2:]; e164="+62"+s[2:]
117
+ elif s.startswith("0"):
118
+ code,country="62","Indonesia"; nat=s; e164="+62"+s[1:]
119
+ else:
120
+ e164=s
121
+ print(f"\nE.164 : {e164 or '(tidak dapat)'}")
122
+ print(f"Negara : {country}{' (+'+code+')' if code else ''}")
123
+ if code == "62":
124
+ op = PREFIX_MAP.get(nat[:4],"(prefix tidak dikenal)")
125
+ print(f"Nasional : {nat}")
126
+ print(f"Operator : {op}")
127
+ print(f"Panjang : {'wajar' if 10<=len(nat)<=13 else 'tidak wajar'}")
128
+
129
+ def feat_entropy():
130
+ s = input("Teks/password: ").strip()
131
+ if not s: return
132
+ freq = Counter(s); n = len(s)
133
+ H = -sum((c/n)*math.log2(c/n) for c in freq.values())
134
+ lo=any(c.islower() for c in s); up=any(c.isupper() for c in s)
135
+ di=any(c.isdigit() for c in s); sy=any(not c.isalnum() for c in s)
136
+ pool=(26 if lo else 0)+(26 if up else 0)+(10 if di else 0)+(33 if sy else 0)
137
+ bits=n*math.log2(pool) if pool else 0
138
+ cat=("Sangat lemah" if bits<28 else "Lemah" if bits<36 else
139
+ "Sedang" if bits<60 else "Kuat" if bits<80 else "Sangat kuat")
140
+ print(f"\nPanjang : {n}")
141
+ print(f"Shannon : {H:.3f} bit/char ({H*n:.1f} bit total)")
142
+ print(f"Pool : {pool}")
143
+ print(f"Kekuatan : {bits:.1f} bit — {cat}")
144
+
145
+ def feat_root():
146
+ claim = input("Ketik u0 (non-root) atau u1 (root): ").strip().lower()
147
+ euid=os.geteuid(); uid=os.getuid(); is_root=(euid==0)
148
+ su=any(os.path.isfile(p) and os.access(p,os.X_OK)
149
+ for p in ["/usr/bin/su","/system/bin/su","/system/xbin/su"])
150
+ sudo=os.path.isfile("/usr/bin/sudo") and os.access("/usr/bin/sudo",os.X_OK)
151
+ print(f"\nEUID : {euid}")
152
+ print(f"UID : {uid}")
153
+ print(f"Status : {'ROOT' if is_root else 'non-root'}")
154
+ print(f"su : {'ada' if su else 'tidak ada'}")
155
+ print(f"sudo : {'ada' if sudo else 'tidak ada'}")
156
+ if claim in ("u0","u1"):
157
+ cr = (claim=="u1")
158
+ print(f"Klaim : {'root' if cr else 'non-root'}")
159
+ print(f"Cocok : {'ya' if cr==is_root else 'TIDAK'}")
160
+
161
+ def feat_author():
162
+ q = input("Nama penulis: ").strip()
163
+ if not q: return
164
+ e = enc(q); ql = q.lower()
165
+ print("\n== OpenAlex ==")
166
+ r = get(f"https://api.openalex.org/authors?per-page=5&search={e}")
167
+ if r and r.status_code == 200:
168
+ try:
169
+ for a in r.json().get("results",[]):
170
+ dn = a.get("display_name","?")
171
+ if not any(w in dn.lower() for w in ql.split()): continue
172
+ aid = a.get("id","").split("/")[-1]
173
+ lki = a.get("last_known_institutions") or []
174
+ inst = lki[0].get("display_name","") if lki else ""
175
+ print(f"- {dn} | karya: {a.get('works_count',0)} | sitasi: {a.get('cited_by_count',0)}")
176
+ if a.get("orcid"): print(f" ORCID : {a['orcid']}")
177
+ if inst: print(f" Afiliasi : {inst}")
178
+ wr = get(f"https://api.openalex.org/works?per-page=4&sort=cited_by_count:desc&filter=author.id:{aid}")
179
+ if wr and wr.status_code == 200:
180
+ try:
181
+ for w in wr.json().get("results",[]):
182
+ print(f" * {w.get('display_name','?')} ({w.get('publication_year','')})")
183
+ except: pass
184
+ print()
185
+ except: print(" Parse gagal.")
186
+ else: print(" Gagal.")
187
+ print("== Crossref ==")
188
+ r2 = get(f"https://api.crossref.org/works?rows=5&select=title,DOI,author&query.author={e}")
189
+ if r2 and r2.status_code == 200:
190
+ try:
191
+ for it in r2.json()["message"]["items"]:
192
+ auth_str = " ".join(f"{a.get('given','')} {a.get('family','')}".lower() for a in it.get("author",[]))
193
+ if not any(w in auth_str for w in ql.split()): continue
194
+ print(f"- {it.get('title',['?'])[0]}\n doi:{it.get('DOI','')}")
195
+ except: print(" Parse gagal.")
196
+ else: print(" Gagal.")
197
+
198
+ def feat_journal():
199
+ q = input("Kata kunci topik: ").strip()
200
+ if not q: return
201
+ e = enc(q)
202
+ print("\n== Semantic Scholar ==")
203
+ r = get(f"https://api.semanticscholar.org/graph/v1/paper/search?query={e}&limit=5&fields=title,year,authors,venue,citationCount,openAccessPdf")
204
+ if r and r.status_code == 200:
205
+ try:
206
+ for p in r.json().get("data",[]):
207
+ print(f"- {p.get('title','?')} ({p.get('year','?')})")
208
+ auths = ", ".join(a.get("name","") for a in p.get("authors",[])[:3])
209
+ if auths: print(f" Penulis : {auths}")
210
+ if p.get("venue"): print(f" Venue : {p['venue']}")
211
+ print(f" Sitasi : {p.get('citationCount','?')}")
212
+ pdf = (p.get("openAccessPdf") or {}).get("url","")
213
+ if pdf: print(f" PDF : {pdf}")
214
+ except: print(" Parse gagal.")
215
+ else: print(" Gagal.")
216
+ print("\n== Crossref ==")
217
+ r2 = get(f"https://api.crossref.org/works?rows=5&select=title,DOI&query={e}")
218
+ if r2 and r2.status_code == 200:
219
+ try:
220
+ for it in r2.json()["message"]["items"]:
221
+ print(f"- {it.get('title',['?'])[0]}\n doi:{it.get('DOI','')}")
222
+ except: print(" Parse gagal.")
223
+ else: print(" Gagal.")
224
+ print("\n== OpenAlex ==")
225
+ r3 = get(f"https://api.openalex.org/works?per-page=5&search={e}")
226
+ if r3 and r3.status_code == 200:
227
+ try:
228
+ for w in r3.json().get("results",[]):
229
+ print(f"- {w.get('display_name','?')} ({w.get('publication_year','')})")
230
+ if w.get("doi"): print(f" {w['doi']}")
231
+ except: print(" Parse gagal.")
232
+ else: print(" Gagal.")
233
+
234
+ def feat_library():
235
+ q = input("Kata kunci buku: ").strip()
236
+ if not q: return
237
+ e = enc(q)
238
+ print("\n== Open Library ==")
239
+ r = get(f"https://openlibrary.org/search.json?limit=5&q={e}")
240
+ if r and r.status_code == 200:
241
+ try:
242
+ for d in r.json().get("docs",[]):
243
+ author = (d.get("author_name") or ["?"])[0]
244
+ print(f"- {d.get('title','?')} — {author} ({d.get('first_publish_year','')})")
245
+ if d.get("key"): print(f" https://openlibrary.org{d['key']}")
246
+ except: print(" Parse gagal.")
247
+ else: print(" Gagal.")
248
+ print("\n== Project Gutenberg ==")
249
+ r2 = get(f"https://gutendex.com/books?search={e}")
250
+ if r2 and r2.status_code == 200:
251
+ try:
252
+ for b in r2.json().get("results",[]):
253
+ author = b.get("authors",[{}])[0].get("name","?") if b.get("authors") else "?"
254
+ print(f"- {b.get('title','?')} — {author}")
255
+ if b.get("id"): print(f" https://gutenberg.org/ebooks/{b['id']}")
256
+ except: print(" Parse gagal.")
257
+ else: print(" Gagal.")
258
+
259
+ def feat_pwned():
260
+ p = input("Password: ").strip()
261
+ if not p: return
262
+ h = hashlib.sha1(p.encode()).hexdigest().upper()
263
+ prefix = h[:5]; suffix = h[5:]
264
+ r = get(f"https://api.pwnedpasswords.com/range/{prefix}")
265
+ if not r or r.status_code != 200:
266
+ print("Gagal menghubungi layanan.")
267
+ return
268
+ count = 0
269
+ for line in r.text.splitlines():
270
+ parts = line.strip().split(":")
271
+ if len(parts) == 2 and parts[0] == suffix:
272
+ count = int(parts[1]); break
273
+ if count > 0:
274
+ print(f"\nDITEMUKAN dalam {count:,} kebocoran. Jangan gunakan.")
275
+ else:
276
+ print("\nTidak ditemukan dalam dataset kebocoran publik.")
277
+
278
+ def feat_institution():
279
+ q = input("Nama institusi: ").strip()
280
+ if not q: return
281
+ e = enc(q); ql = q.lower(); qu = q.upper()
282
+ print("\n== OpenAlex ==")
283
+ r = get(f"https://api.openalex.org/institutions?per-page=5&search={e}")
284
+ if r and r.status_code == 200:
285
+ try:
286
+ found = False
287
+ for it in r.json().get("results",[]):
288
+ dn = it.get("display_name","?")
289
+ if not any(w in dn.lower() for w in ql.split()): continue
290
+ found = True
291
+ print(f"- {dn}")
292
+ print(f" Karya : {it.get('works_count',0)} | Sitasi: {it.get('cited_by_count',0)}")
293
+ if it.get("homepage_url"): print(f" Web : {it['homepage_url']}")
294
+ if not found: print(" Tidak ditemukan (wajar untuk SMA/SMK).")
295
+ except: print(" Parse gagal.")
296
+ else: print(" Gagal.")
297
+ is_school = any(k in qu for k in ["SMA","SMK","SMP","MAN","MTsN","MIN","SMAN","SMKN","SMPN"])
298
+ is_campus = any(k in qu for k in ["STMIK","UNIVERSITAS","POLTEK","INSTITUT","SEKOLAH TINGGI","AMIK","STIE","STIKES","UIN","AKADEMI"])
299
+ gs = enc('"'+q+'"')
300
+ print(f"\nGoogle Scholar : https://scholar.google.com/scholar?q={gs}")
301
+ print(f"Garuda Kemdikbud : https://garuda.kemdikbud.go.id/search?q={e}")
302
+ print(f"SINTA Kemdikbud : https://sinta.kemdikbud.go.id/affiliations?q={e}")
303
+ if is_campus:
304
+ print(f"PDDikti : https://pddikti.kemdikbud.go.id/search/{e}")
305
+ if is_school:
306
+ print(f"Dapodik : https://sekolah.data.kemdikbud.go.id/index.php/chome/profil/?nama={e}")
307
+ print(f"Referensi Kemdikbud: https://referensi.data.kemdikbud.go.id")
308
+ print(f"Prestasi/berita : https://www.google.com/search?q={gs}+prestasi+OR+juara")
309
+ print(f"Jurnal/skripsi PDF : https://www.google.com/search?q={gs}+jurnal+OR+skripsi+filetype:pdf")
310
+
311
+ def feat_mahasiswa():
312
+ q = input("Nama mahasiswa: ").strip()
313
+ if not q: return
314
+ e = enc(q); ql = q.lower()
315
+ print("\n== PDDikti (status mahasiswa) ==")
316
+ r = get(f"https://api.pddikti.kemdikbud.go.id/api/pencarian/mhs/{e}")
317
+ if r and r.status_code == 200:
318
+ try:
319
+ data = r.json()
320
+ mahasiswa = data.get("mahasiswa", data.get("data", []))
321
+ if not mahasiswa:
322
+ print(" Tidak ditemukan.")
323
+ for m in mahasiswa[:10]:
324
+ nama = m.get("nama","?")
325
+ pt = m.get("namapt","?")
326
+ prodi = m.get("namaprodi","?")
327
+ status = m.get("statusmahasiswa", m.get("status","?"))
328
+ nim = m.get("nim","")
329
+ print(f"- {nama}")
330
+ print(f" NIM : {nim}")
331
+ print(f" PT : {pt}")
332
+ print(f" Prodi : {prodi}")
333
+ print(f" Status : {status}")
334
+ print()
335
+ except: print(" Parse gagal.")
336
+ else:
337
+ print(" Layanan PDDikti tidak merespons.")
338
+ print(f" Cari manual : https://pddikti.kemdikbud.go.id/search/{e}")
339
+ print("== Karya/publikasi mahasiswa ==")
340
+ r2 = get(f"https://api.openalex.org/authors?per-page=5&search={e}")
341
+ if r2 and r2.status_code == 200:
342
+ try:
343
+ for a in r2.json().get("results",[]):
344
+ dn = a.get("display_name","?")
345
+ if not any(w in dn.lower() for w in ql.split()): continue
346
+ aid = a.get("id","").split("/")[-1]
347
+ lki = a.get("last_known_institutions") or []
348
+ inst = lki[0].get("display_name","") if lki else ""
349
+ print(f"- {dn} | karya: {a.get('works_count',0)} | sitasi: {a.get('cited_by_count',0)}")
350
+ if inst: print(f" Afiliasi : {inst}")
351
+ wr = get(f"https://api.openalex.org/works?per-page=4&sort=cited_by_count:desc&filter=author.id:{aid}")
352
+ if wr and wr.status_code == 200:
353
+ try:
354
+ for w in wr.json().get("results",[]):
355
+ print(f" * {w.get('display_name','?')} ({w.get('publication_year','')})")
356
+ except: pass
357
+ print()
358
+ except: pass
359
+ gs = enc('"'+q+'"')
360
+ print(f"Garuda Kemdikbud : https://garuda.kemdikbud.go.id/search?q={gs}")
361
+ print(f"Google Scholar : https://scholar.google.com/scholar?q={gs}")
362
+
363
+ def feat_blockchain():
364
+ print("\nPilih jaringan:")
365
+ print(" 1. Bitcoin (BTC)")
366
+ print(" 2. Ethereum (ETH)")
367
+ print(" 3. Polygon (MATIC)")
368
+ print(" 4. BNB Smart Chain (BSC)")
369
+ net = input("Pilih: ").strip()
370
+ addr = input("Alamat wallet atau TX hash: ").strip()
371
+ if not addr: return
372
+
373
+ is_tx = len(addr) > 50
374
+
375
+ if net == "1":
376
+ if is_tx:
377
+ r = get(f"https://blockstream.info/api/tx/{addr}")
378
+ if r and r.status_code == 200:
379
+ try:
380
+ d = r.json()
381
+ print(f"\nTX ID : {d.get('txid','?')}")
382
+ print(f"Status : {'Terkonfirmasi' if d.get('status',{}).get('confirmed') else 'Pending'}")
383
+ print(f"Fee : {d.get('fee',0)} sat")
384
+ vin_total = sum(v.get("prevout",{}).get("value",0) for v in d.get("vin",[]))
385
+ vout_total = sum(v.get("value",0) for v in d.get("vout",[]))
386
+ print(f"Input : {vin_total/1e8:.8f} BTC")
387
+ print(f"Output : {vout_total/1e8:.8f} BTC")
388
+ if vin_total > 10*1e8:
389
+ print(f"PERINGATAN : transaksi besar (>{vin_total/1e8:.2f} BTC) — patut diinvestigasi")
390
+ except: print(" Parse gagal.")
391
+ else: print(" TX tidak ditemukan.")
392
+ else:
393
+ r = get(f"https://blockstream.info/api/address/{addr}")
394
+ if r and r.status_code == 200:
395
+ try:
396
+ d = r.json()
397
+ cs = d.get("chain_stats",{}); ms = d.get("mempool_stats",{})
398
+ funded = cs.get("funded_txo_sum",0)
399
+ spent = cs.get("spent_txo_sum",0)
400
+ balance = (funded - spent)/1e8
401
+ tx_count = cs.get("tx_count",0)
402
+ print(f"\nAlamat : {addr}")
403
+ print(f"Balance : {balance:.8f} BTC")
404
+ print(f"Total TX : {tx_count}")
405
+ print(f"Masuk : {funded/1e8:.8f} BTC")
406
+ print(f"Keluar : {spent/1e8:.8f} BTC")
407
+ if balance > 100:
408
+ print(f"PERINGATAN : saldo sangat besar ({balance:.2f} BTC)")
409
+ if tx_count > 10000:
410
+ print(f"PERINGATAN : volume TX sangat tinggi — kemungkinan mixing/tumbler")
411
+ r2 = get(f"https://blockstream.info/api/address/{addr}/txs")
412
+ if r2 and r2.status_code == 200:
413
+ txs = r2.json()[:5]
414
+ print(f"\n5 TX terakhir:")
415
+ for tx in txs:
416
+ tid = tx.get("txid","?")[:20]+"..."
417
+ status = "Confirmed" if tx.get("status",{}).get("confirmed") else "Pending"
418
+ fee = tx.get("fee",0)
419
+ print(f" {tid} | {status} | fee: {fee} sat")
420
+ except: print(" Parse gagal.")
421
+ else: print(" Alamat tidak ditemukan.")
422
+ print(f"\nEksplorer : https://blockstream.info/address/{addr}")
423
+ print(f"OXT (analisis cluster BTC) : https://oxt.me/address/{addr}")
424
+
425
+ elif net in ("2","3","4"):
426
+ if net == "2":
427
+ api_base = "https://api.etherscan.io/api"
428
+ explorer = f"https://etherscan.io/address/{addr}"
429
+ chain_name = "Ethereum"
430
+ elif net == "3":
431
+ api_base = "https://api.polygonscan.com/api"
432
+ explorer = f"https://polygonscan.com/address/{addr}"
433
+ chain_name = "Polygon"
434
+ else:
435
+ api_base = "https://api.bscscan.com/api"
436
+ explorer = f"https://bscscan.com/address/{addr}"
437
+ chain_name = "BNB Smart Chain"
438
+
439
+ print(f"\n== {chain_name} ==")
440
+
441
+ if is_tx:
442
+ r = get(f"{api_base}?module=proxy&action=eth_getTransactionByHash&txhash={addr}&apikey=YourApiKeyToken")
443
+ if r and r.status_code == 200:
444
+ try:
445
+ d = r.json().get("result",{})
446
+ if d:
447
+ val = int(d.get("value","0x0"),16)/1e18
448
+ print(f"TX Hash : {d.get('hash','?')}")
449
+ print(f"Dari : {d.get('from','?')}")
450
+ print(f"Ke : {d.get('to','?')}")
451
+ print(f"Nilai : {val:.6f} coin")
452
+ print(f"Gas : {int(d.get('gas','0x0'),16)}")
453
+ if val > 1000:
454
+ print(f"PERINGATAN : transaksi sangat besar ({val:.2f} coin)")
455
+ else:
456
+ print(" TX tidak ditemukan.")
457
+ except: print(" Parse gagal.")
458
+ else:
459
+ r = get(f"{api_base}?module=account&action=balance&address={addr}&tag=latest&apikey=YourApiKeyToken")
460
+ if r and r.status_code == 200:
461
+ try:
462
+ bal = int(r.json().get("result","0"))/1e18
463
+ print(f"Alamat : {addr}")
464
+ print(f"Balance : {bal:.6f} coin")
465
+ if bal > 10000:
466
+ print(f"PERINGATAN : saldo sangat besar ({bal:.2f} coin)")
467
+ except: print(" Parse gagal.")
468
+
469
+ r2 = get(f"{api_base}?module=account&action=txlist&address={addr}&startblock=0&endblock=99999999&sort=desc&apikey=YourApiKeyToken")
470
+ if r2 and r2.status_code == 200:
471
+ try:
472
+ txs = r2.json().get("result",[])
473
+ if isinstance(txs, list):
474
+ print(f"Total TX : {len(txs)}")
475
+ rapid = 0
476
+ prev_time = None
477
+ for tx in txs[:5]:
478
+ val = int(tx.get("value","0"))/1e18
479
+ frm = tx.get("from","?")
480
+ to = tx.get("to","?")
481
+ ts = tx.get("timeStamp","")
482
+ err = tx.get("isError","0")
483
+ print(f" {frm[:10]}...→{to[:10]}... | {val:.4f} | {'GAGAL' if err=='1' else 'OK'}")
484
+ if prev_time and ts:
485
+ diff = abs(int(prev_time)-int(ts))
486
+ if diff < 60: rapid += 1
487
+ prev_time = ts
488
+ if rapid >= 3:
489
+ print(f"PERINGATAN : {rapid} TX dalam interval <60 detik — pola mencurigakan")
490
+ if len(txs) > 5000:
491
+ print(f"PERINGATAN : volume TX sangat tinggi ({len(txs)}) — kemungkinan bot/mixer")
492
+ except: print(" Parse gagal.")
493
+
494
+ print(f"\nEksplorer : {explorer}")
495
+ print(f"Lapor node jahat Ethereum : https://github.com/ethereum/go-ethereum/issues")
496
+ print(f"Lapor ke Etherscan : https://etherscan.io/contactus")
497
+
498
+ else:
499
+ print("Pilihan tidak valid.")
500
+ return
501
+
502
+ print(f"\nLaporan pencucian uang (Indonesia) : https://www.ppatk.go.id/layanan/")
503
+ print(f"Chainalysis (investigasi lanjut) : https://www.chainalysis.com")
504
+ print(f"Crystal Blockchain (tracing) : https://crystalblockchain.com")
505
+
506
+ MENU = [
507
+ ("Cek reputasi URL", feat_url),
508
+ ("Cek format nomor rekening (offline)", feat_account),
509
+ ("Metadata nomor telepon", feat_phone),
510
+ ("Hitung entropi string/password", feat_entropy),
511
+ ("Cek status root/privilege", feat_root),
512
+ ("Cari karya/publikasi penulis", feat_author),
513
+ ("Cari jurnal & referensi ilmiah", feat_journal),
514
+ ("Cari pustaka/buku", feat_library),
515
+ ("Cek password bocor (k-anonymity HIBP)", feat_pwned),
516
+ ("Cari sumber publik institusi Indonesia", feat_institution),
517
+ ("Cari mahasiswa & karyanya (PDDikti)", feat_mahasiswa),
518
+ ("Investigasi Blockchain (BTC/ETH/MATIC/BSC)", feat_blockchain),
519
+ ]
520
+
521
+ def main():
522
+ while True:
523
+ print("\n" + "="*50)
524
+ print(" OCTROUBLE v2.0 — OSINT Publik")
525
+ print("="*50)
526
+ for i,(label,_) in enumerate(MENU,1):
527
+ print(f" {i:2}. {label}")
528
+ print(" 0. Keluar")
529
+ pick = input("Pilih: ").strip()
530
+ if pick == "0": break
531
+ try:
532
+ idx = int(pick)-1
533
+ if 0 <= idx < len(MENU): MENU[idx][1]()
534
+ else: print("Pilihan tidak valid.")
535
+ except ValueError:
536
+ print("Masukkan angka.")
537
+
538
+ if __name__ == "__main__":
539
+ main()
@@ -0,0 +1,16 @@
1
+ Metadata-Version: 2.4
2
+ Name: octrouble
3
+ Version: 2.0.0
4
+ Summary: OSINT publik: URL, rekening, telepon, entropi, publikasi, institusi Indonesia, Blockchain
5
+ Requires-Python: >=3.8
6
+ Description-Content-Type: text/markdown
7
+ Requires-Dist: requests
8
+
9
+ # Octrouble
10
+ OSINT publik berbasis terminal.
11
+
12
+ ## Install
13
+ pip install octrouble
14
+
15
+ ## Jalankan
16
+ octrouble
@@ -0,0 +1,10 @@
1
+ README.md
2
+ pyproject.toml
3
+ octrouble/__init__.py
4
+ octrouble/__main__.py
5
+ octrouble.egg-info/PKG-INFO
6
+ octrouble.egg-info/SOURCES.txt
7
+ octrouble.egg-info/dependency_links.txt
8
+ octrouble.egg-info/entry_points.txt
9
+ octrouble.egg-info/requires.txt
10
+ octrouble.egg-info/top_level.txt
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ octrouble = octrouble.__main__:main
@@ -0,0 +1 @@
1
+ requests
@@ -0,0 +1 @@
1
+ octrouble
@@ -0,0 +1,14 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61.0", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "octrouble"
7
+ version = "2.0.0"
8
+ description = "OSINT publik: URL, rekening, telepon, entropi, publikasi, institusi Indonesia, Blockchain"
9
+ readme = "README.md"
10
+ requires-python = ">=3.8"
11
+ dependencies = ["requests"]
12
+
13
+ [project.scripts]
14
+ octrouble = "octrouble.__main__:main"
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+