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.
- octrouble-2.0.0/PKG-INFO +16 -0
- octrouble-2.0.0/README.md +8 -0
- octrouble-2.0.0/octrouble/__init__.py +0 -0
- octrouble-2.0.0/octrouble/__main__.py +539 -0
- octrouble-2.0.0/octrouble.egg-info/PKG-INFO +16 -0
- octrouble-2.0.0/octrouble.egg-info/SOURCES.txt +10 -0
- octrouble-2.0.0/octrouble.egg-info/dependency_links.txt +1 -0
- octrouble-2.0.0/octrouble.egg-info/entry_points.txt +2 -0
- octrouble-2.0.0/octrouble.egg-info/requires.txt +1 -0
- octrouble-2.0.0/octrouble.egg-info/top_level.txt +1 -0
- octrouble-2.0.0/pyproject.toml +14 -0
- octrouble-2.0.0/setup.cfg +4 -0
octrouble-2.0.0/PKG-INFO
ADDED
|
@@ -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
|
|
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 @@
|
|
|
1
|
+
|
|
@@ -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"
|