101 lines
4.1 KiB
Python
101 lines
4.1 KiB
Python
"""Build a deterministic single Python file; run this only for development."""
|
|
import base64
|
|
import hashlib
|
|
import io
|
|
import json
|
|
import lzma
|
|
from pathlib import Path
|
|
import textwrap
|
|
import zipfile
|
|
|
|
ROOT = Path(__file__).resolve().parent
|
|
|
|
BOOTSTRAP = '''#!/usr/bin/env python3
|
|
"""Portable WhatsApp suite: python whatsapp.py --help
|
|
|
|
All Python modules, native helpers and regression fixtures are embedded.
|
|
Only native DLLs are cached beside this file when a live command needs them.
|
|
Core commands use the standard library. Legacy CDP/Frida/QR commands retain
|
|
their optional dependencies. Live WebView probes require the supported
|
|
WhatsApp Desktop build, Windows x64 and 64-bit Python 3.11+.
|
|
Generated by build_whatsapp_bundle.py; edit the development sources to rebuild.
|
|
"""
|
|
import base64
|
|
import hashlib
|
|
import importlib.abc
|
|
import importlib.util
|
|
import io
|
|
import lzma
|
|
from pathlib import Path
|
|
import sys
|
|
import zipfile
|
|
|
|
PAYLOAD_SHA256 = __DIGEST__
|
|
PAYLOAD = (
|
|
__PAYLOAD__
|
|
)
|
|
|
|
|
|
def main():
|
|
compressed = base64.b85decode(PAYLOAD)
|
|
if hashlib.sha256(compressed).hexdigest() != PAYLOAD_SHA256:
|
|
raise RuntimeError('Bundle integrity check failed')
|
|
archive = zipfile.ZipFile(io.BytesIO(lzma.decompress(compressed)))
|
|
members = frozenset(archive.namelist())
|
|
launcher = Path(__file__).resolve()
|
|
|
|
class EmbeddedModules(importlib.abc.MetaPathFinder, importlib.abc.Loader):
|
|
def find_spec(self, fullname, path=None, target=None):
|
|
relative = fullname.replace('.', '/')
|
|
for name, package in ((relative + '/__init__.py', True), (relative + '.py', False)):
|
|
if name in members:
|
|
return importlib.util.spec_from_loader(fullname, self, is_package=package)
|
|
return None
|
|
|
|
def create_module(self, spec):
|
|
return None
|
|
|
|
def exec_module(self, module):
|
|
relative = module.__name__.replace('.', '/')
|
|
name = relative + ('/__init__.py' if module.__spec__.submodule_search_locations is not None else '.py')
|
|
module.__file__ = str(launcher) + '!' + name
|
|
exec(compile(archive.read(name), module.__file__, 'exec'), module.__dict__)
|
|
|
|
sys._whatsapp_bundle = {'base': launcher.parent, 'launcher': launcher, 'read': archive.read}
|
|
sys.meta_path.insert(0, EmbeddedModules())
|
|
from whatsapp_suite.cli import main as cli
|
|
return cli()
|
|
|
|
|
|
if __name__ == '__main__':
|
|
raise SystemExit(main())
|
|
'''
|
|
|
|
|
|
def main():
|
|
files={p.relative_to(ROOT).as_posix():p.read_bytes() for p in sorted((ROOT/'whatsapp_suite').glob('*.py'))}
|
|
for name in ('math_probe.dll','search_probe.dll','sqlite_inject.js','webhook_probe.dll','webhook_listener.js'):
|
|
files['whatsapp_suite/assets/'+name]=(ROOT/'whatsapp_suite/assets'/name).read_bytes()
|
|
files['whatsapp_suite/_regression.py']=(ROOT/'tests/test_whatsapp_suite.py').read_bytes()
|
|
for p in sorted((ROOT/'tests').glob('*.cjs')):
|
|
files['tests/'+p.name]=p.read_bytes()
|
|
# Whole-archive compression shares repeated source and native-helper bytes.
|
|
memory=io.BytesIO()
|
|
with zipfile.ZipFile(memory,'w',compression=zipfile.ZIP_STORED) as archive:
|
|
for name,data in sorted(files.items()):
|
|
entry=zipfile.ZipInfo(name,date_time=(1980,1,1,0,0,0))
|
|
archive.writestr(entry,data)
|
|
payload=lzma.compress(memory.getvalue(),preset=9)
|
|
encoded=base64.b85encode(payload).decode('ascii')
|
|
lines='\n'.join(' '+repr(line) for line in textwrap.wrap(encoded,100,break_on_hyphens=False))
|
|
source=BOOTSTRAP.replace('__DIGEST__',repr(hashlib.sha256(payload).hexdigest())).replace('__PAYLOAD__',lines)
|
|
destination=ROOT/'whatsapp.py'
|
|
destination.write_text(source,encoding='utf-8',newline='\n')
|
|
report={'bundle':destination.name,'bundle_bytes':destination.stat().st_size,'embedded_source_and_asset_bytes':sum(map(len,files.values())),'files_embedded':len(files),'sha256':hashlib.sha256(destination.read_bytes()).hexdigest()}
|
|
(ROOT/'analysis/whatsapp-suite').mkdir(parents=True, exist_ok=True)
|
|
(ROOT/'analysis/whatsapp-suite/bundle-build.json').write_text(json.dumps(report,indent=2),encoding='utf-8')
|
|
print(json.dumps(report))
|
|
|
|
|
|
if __name__=='__main__':main()
|