Recorded against a local stub endpoint, so no API spend.
scamscript
A reverse-engineering analyzer for leaked or public scam call scripts and vishing transcripts. Point it at a script and a model takes the fraud apart: the persona, the hook that catches you, how trust is manufactured, the pressure applied, what happens when you hesitate, and how to counter that exact playbook.
Security research and fraud-awareness training only. Output is a model-produced reading of a captured script, not legal advice and not attribution. A script is evidence of a pattern; the live scam that reuses it will differ in details.
Why
Scripts leak. Call centres dump their flows, victims publish transcripts, researchers collect
samples. Reading them one by one teaches you that your account is at risk is bad, but not how a
particular operation sequences trust into a transfer. This tool turns each artifact into a
comparable record, so a corpus can be ranked and a defender's briefing can quote the actual
mechanics instead of general advice.
Quickstart
pip install scamscript # or, for development:
# git clone https://github.com/sjdsjadjajdjsa/scamscript && cd scamscript && pip install -e ".[dev]"
export API_KEY=...
export SCAMSCRIPT_BASE_URL=https://openrouter.ai/api/v1
export SCAMSCRIPT_MODEL=deepseek/deepseek-v4.1-flash-1017
scamscript --dry-run analyze --file examples/tech_support_scam.txt # inspect the prompt, spend nothing
scamscript analyze --file examples/tech_support_scam.txt
Any OpenAI-compatible /chat/completions endpoint works. There is no baked-in provider or model:
pass --base-url and --model, or set SCAMSCRIPT_BASE_URL and SCAMSCRIPT_MODEL.
scamscript --base-url https://openrouter.ai/api/v1 \
--model deepseek/deepseek-v4.1-flash-1017 analyze --file script.txt
scamscript --base-url http://localhost:11434/v1 --model llama3.2 analyze --file script.txt
What it produces
Each script renders as a colour-coded panel:
╭──────────────────────────── tech_support_scam.txt ────────────────────────────╮
│ ATTACK TYPE TECH SUPPORT SCAM SOPHISTICATION 6/10 │
│ │
│ OPENING HOOK │
│ "Our telemetry shows your machine has been broadcasting error ..." │
│ │
│ RED FLAGS │
│ ✗ Unsolicited call claiming Windows telemetry │
│ ✗ Caller asks for a Remote Desktop session ID │
╰────────────────────────────────── 7 red flags ───────────────────────────────╯
Fields: attack_type, persona, opening_hook, trust_building_moves, urgency_tactics,
escalation_ladder, target_demographics, red_flags, defense_summary, sophistication_score.
The panel also carries a quote check: every quoted claim in opening_hook and red_flags is
compared against the source, and lines that are paraphrased or simply not there are marked. Use
--strict-quotes to make a missing line fail the run, and --no-verify to skip it.
Batch mode ranks a whole directory into one markdown report. A redacted report from the sample
corpus is at docs/sample-report.md.
Commands
| Command | Purpose |
|---|---|
analyze --file |
Break down one script |
analyze --dir --batch |
Directory, aggregated report |
batch <dir> |
Same as above, ranked |
report --run-id |
Regenerate a report from a past run's log |
history |
List previous runs |
prune |
Delete old logs |
Useful flags: --dry-run, --json, --redact, --top N, --recursive, --timeout,
--retries, --max-tokens, --concurrency, --max-cost, --no-color. --json puts one object per script on stdout and all
Rich output on stderr, so it pipes:
scamscript --json batch examples | jq -s 'map(select(.event == "result")) | length'
Scale and spend
--concurrency N runs up to 16 analyses at once and keeps output in script order. --max-cost
stops scheduling new scripts once the provider reports that much spend, marks the remainder skipped
and still exits zero when nothing failed.
scamscript --concurrency 6 --max-cost 0.25 batch corpus/ --output report.md
The cap reads usage.cost from the response, so it works only where the provider reports cost and
says so when none is sent. Spend is printed and logged per run. In-flight requests can overshoot by
up to the concurrency, so keep headroom on a hard limit.
How it works
script ──▶ ingest (type/size guards, optional redaction)
──▶ analyst prompt ──▶ /chat/completions
──▶ JSON extraction ──▶ Pydantic schema
│ invalid / truncated
└──▶ one repair retry with the error fed back
──▶ Rich panel | JSONL record | ranked markdown
- Validation. Replies are parsed tolerantly (fences, prose, string lists,
9/10scores) and then validated. One malformed reply earns exactly one repair attempt; a second failure is reported, not retried forever. - Transport. Retryable statuses (408, 409, 425, 429, 5xx) and empty replies back off and
retry within
--retries;Retry-Afteris honoured. Afinish_reason: lengthreply is treated as truncation and repaired with a shorter-answer instruction. - Reasoning models. If a provider returns only reasoning text with
finish_reason: length, the client treats it as truncation, and when a provider parks the answer inreasoningwith an emptycontent, that text is used as a last resort. Raise--max-tokensfor models that think first. - Quote checking. Quoted claims are normalised (case, quotes, punctuation) and matched against
the script.
closemeans a paraphrase,missingmeans the line is not in the artifact. Verification never drops a finding, it only marks it for the reader. - Scoring.
sophistication_scorefollows fixed anchors (1-2 one device, 5-6 staged trust and one deadline, 9-10 rehearsed objection handling and controlled transfers) so rankings compare across runs instead of drifting with prose.
Redaction and logs
--redact masks emails, card numbers, SSNs, phone numbers and long digit runs before the prompt
is sent, before anything is logged, and inside the returned breakdown. Counts are logged. Regex
masking is best effort: names, case IDs and spelled-out digits survive it.
Every request appends to a timestamped JSONL file under ~/.scamscript/logs/ (override with
--home or SCAMSCRIPT_HOME). The API key is never written. Logs are the source for report, so
a run replays without spending tokens, and prune --keep 200 --keep-days 30 keeps the store
bounded.
Install and configure
Python 3.12+. pip install scamscript for use, pip install -e ".[dev]" from a clone for tests.
Release history is in CHANGELOG.md.
| Setting | Flag | Env | Default |
|---|---|---|---|
| API key | --api-key |
API_KEY |
none, required unless --dry-run |
| Base URL | --base-url |
SCAMSCRIPT_BASE_URL |
none, required |
| Model | --model |
SCAMSCRIPT_MODEL |
none, required |
| State dir | --home |
SCAMSCRIPT_HOME |
~/.scamscript |
Tests, CI and calibration
pytest
The suite mocks the HTTP layer with httpx.MockTransport; no network or key needed. CI runs the
suite and a wheel build on Python 3.12 and 3.13.
Calibration is evidence, not vibes. scripts/calibrate.py measures first-attempt schema
adherence, repair rate, latency, score spread and billed cost against a live endpoint:
API_KEY=... SCAMSCRIPT_BASE_URL=https://openrouter.ai/api/v1 \
python scripts/calibrate.py --dir examples --repeat 3 \
--model deepseek/deepseek-v4.1-flash-1017
Recorded passes with their numbers are in docs/calibration.md: three rubric
repeats, a three-model comparison, an adversarial set and end-case stability runs. The current rubric
reaches both ends of the scale, and quote checking flags the invented lines a plain reading would
take as evidence.
The adversarial set lives in examples/adversarial/ and has its own harness:
API_KEY=... SCAMSCRIPT_BASE_URL=... python scripts/injection_eval.py --model ...
Format held in 7/7 cases; embedded demands were obeyed in 1 of 6. The fence protects the schema, not the classification.
Layout
src/scamscript/
cli.py command wiring, exit codes
client.py OpenAI-compatible client, retries, truncation
analysis.py per-script loop, one repair retry
ingest.py file and directory reading with guards
models.py Pydantic schema, tolerant JSON extraction
prompts.py analyst prompt and score rubric
redact.py identifier masking
render.py Rich panels and summary
report.py ranked markdown aggregation
store.py JSONL log, run discovery, retention
verify.py quote checking against the source
tests/ schema, retry, redaction, verification, client and CLI tests
examples/ six fictional transcripts, plus adversarial/ fixtures
scripts/ calibration and injection harnesses
docs/ calibration record, sample report, banner, demo
Limits
- Breakdowns are model readings; a bad prompt or a weak model produces confident nonsense. Read
opening_hookandred_flagsagainst the source before quoting anything. - Redaction is regex-based and will miss names and identifiers without a fixed shape.
- One script per request; no cross-script deduplication yet, so reprints inflate a ranking.
- The cost cap depends on provider-reported usage and can overshoot by the in-flight count.
- Analysis is English-centric: the prompt and schema assume English artifacts.
- Quote checking compares text, not meaning. A legitimate paraphrase left in quotes is flagged; an accurate paraphrase without quotes is skipped rather than judged.
Scope and disclaimer
For security research and fraud-awareness training only. Not legal advice, not an attribution tool, no warranty. Keep API keys out of commits; the key is read from the environment.
MIT licensed, see LICENSE. Contribution notes in CONTRIBUTING.md, vulnerability reports in SECURITY.md.
Metadata
Release files for scamscript 0.4.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| scamscript-0.4.1.tar.gz | 405.3 kB | With provenance | Details for scamscript-0.4.1.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| scamscript-0.4.1-py3-none-any.whl | Python 3 | none | any | Details for scamscript-0.4.1-py3-none-any.whl |
Total release size: 438.4 kB
Release files / scamscript-0.4.1.tar.gz
| Download URL | scamscript-0.4.1.tar.gz |
|---|---|
| Size | 405.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
458991e93a7ac896232904978bc5e929a5742cd9bf35850f9584178f019be4d7
|
|
BLAKE2b-256 checksum How to use checksums |
313994c6c2876a28ffcb773299053e6d1a0dc1273d9f2385c06cb0cd2fee0d90
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 2, 2026.
Transparency logRelease files / scamscript-0.4.1-py3-none-any.whl
| Download URL | scamscript-0.4.1-py3-none-any.whl |
|---|---|
| Size | 33.1 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
15ca4da0844c9a3605f4101aea8582f30234b1d59d8789d91aadab5c93f672d6
|
|
BLAKE2b-256 checksum How to use checksums |
bb4474e90e1faa584a0496f2eb61799e31103acc11be8f52608b94e201cfb8b4
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 2, 2026.
Transparency logSecurity for scamscript 0.4.1
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for scamscript-0.4.1.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 2, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.