#!/usr/bin/env python3 """Extract the Boti Education app API endpoints from decompiled Capacitor JS chunks. Usage: python3 extract_endpoints.py [--markdown] Scans *.js for the Angular ApiService call signature: .post(, "endpoint") / .get(, "endpoint") and emits a deduplicated endpoint -> methods table. """ import re import sys import glob import os import argparse CALL = re.compile( r'\.(post|get|getBlob)\(\s*' r'(?:"[^"]*"|`[^`]*`|[A-Za-z_$][\w$]*(?:\s*\.\s*[\w$]+)*(?:\([^()]*\))?|\{[^{}]*\})' r'\s*,\s*"([^"]*)"' ) def scan(directory): found = {} # endpoint -> {method: [chunks]} for path in sorted(glob.glob(os.path.join(directory, '*.js'))): chunk = os.path.basename(path) src = open(path, encoding='utf8', errors='replace').read() for m in CALL.finditer(src): method, ep = m.group(1), m.group(2) d = found.setdefault(ep, {}) d.setdefault(method, []).append(chunk) return found def main(): ap = argparse.ArgumentParser() ap.add_argument('directory') ap.add_argument('--markdown', action='store_true') args = ap.parse_args() found = scan(args.directory) if not found: sys.exit('no endpoints found, wrong directory?') if args.markdown: print(f'# API endpoints extracted from {os.path.basename(args.directory)}') print() print(f'{len(found)} unique endpoints.') print() print('| endpoint | methods | seen in chunk(s) |') print('|---|---|---|') for ep in sorted(found): methods = ', '.join(sorted(found[ep])) chunks = ', '.join(sorted({c for ms in found[ep].values() for c in ms})[:3]) print(f'| {ep} | {methods} | {chunks} |') else: for ep in sorted(found): print(ep, sorted(found[ep])) print(f'TOTAL endpoints: {len(found)}', file=sys.stderr) if __name__ == '__main__': main()