Discord bot for detecting known the Mr Beast crypto casino scam where bots or compromised accounts send an empty message with 4 or 2 images.
Please note that everything is slopped.
Slopped description
The project is intentionally conservative at runtime:
- exact raw file matches are always treated as
scam - visual matching is scam-only
borderlineresults are logged but never enforcedsaferesults do nothing
The current matcher is built around small curated image families under datasets/scam, not OCR or a general ML model.
When a user posts an image attachment in a guild where scanning is enabled, the bot:
- downloads the image
- computes visual features
- compares the image against the scam dataset
- classifies it as
safe,borderline, orscam - enforces only when the classification is
scam
If enforcement happens, the bot:
- deletes the message
- DMs the user a configurable message
- kicks the member
- writes a moderation log entry if a log channel is configured
If the image is only borderline, the bot writes a log entry and stops there.
The bot exposes one command: /nobeast
Supported subcommands:
statusenabledisabledryrun viewdryrun enabledryrun disablemessage viewmessage setmessage resetinvite viewinvite setinvite clearlogchannel viewlogchannel setlogchannel clear
Notes:
- only members with
Manage Guildcan use the command - custom kick messages must include
{serverName} - invite URLs must be Discord invite URLs
- log channels must be guild text channels
src/moderation.ts applies these filters before matching:
- ignore DMs
- ignore webhook messages
- ignore bot messages
- ignore servers where scanning is disabled
- only inspect image attachments
- ignore attachments larger than
15 MB
Classification handling:
safe: no actionborderline: log onlyscam: delete, DM, kick, and log
Dry-run mode:
- never deletes
- never DMs
- never kicks
- still logs the analysis result
The core matcher lives in src/matcher.ts.
The classification pipeline is:
exact-rawfamily-consensus
There is no normalized-SHA match stage anymore, and there is no permissive template-only fallback.
The first check is a raw SHA-256 hash on the original attachment bytes.
If the exact file exists in the scam dataset:
- classification is immediately
scam - stage is
exact-raw
If raw SHA does not match, the matcher extracts normalized visual features and runs family-aware comparison.
The image is assigned an archetype:
x-postwithdrawal-proof
This is currently based on aspect ratio, using a split threshold defined in src/constants.ts.
Then the matcher:
- scores all scam family centroids within the same archetype
- keeps a shortlist of the best families
- scores the candidate against real reference members from the top family
- applies consensus rules
- returns
safe,borderline, orscam
The result includes:
classificationstagematchedFamilyIdconfidenceroiVotesfamilyCandidatesdetails
src/image-hash.ts normalizes every image to a 256x256 grayscale canvas using sharp.
The image is resized with fit: "contain", which preserves layout and pads empty space instead of stretching the screenshot.
Extracted features:
rawSha256: exact original-byte hashpHash: perceptual hash from a DCT over a32x32grayscale imagedHash: difference hash from a9x8grayscale imageedgeHash: perceptual hash of a simple edge imagelumaGrid:16x16block-mean grayscale summaryroiSignatures:8x8block-mean summaries for a few selected ROIs
ROI stands for Region of Interest.
In this project, ROIs are rectangular parts of the normalized screenshot that are especially useful for distinguishing scam families. Instead of trusting whole-image similarity alone, the matcher also asks whether these more discriminative regions agree.
src/dataset.ts treats each top-level folder in datasets/scam as a scam family.
For example:
datasets/scam/masowin/...datasets/scam/hesobia/...
Each image becomes a DatasetFingerprint with:
familyIdarchetyperawSha256pHashdHashedgeHashlumaGridroiSignatures
For each archetype:
- candidate
64x64windows are laid out on the normalized256x256image - each candidate window is converted into a compact ROI signature
- windows are scored by how well they separate families while staying consistent within a family
- the best 4 windows are kept
Each family/archetype pair gets a centroid model with:
- centroid aspect ratio
- centroid pHash
- centroid dHash
- centroid edge hash
- centroid luma grid
- centroid ROI signatures
- thresholds derived from in-family spread
Families with fewer than 2 members are marked borderlineOnly and cannot pass the main acceptance rule directly.
The matcher is intentionally conservative.
Main acceptance rule:
- top family is not
borderlineOnly - centroid score is within the family threshold
- at least 3 ROI votes pass
- at least 1 strong supporting family reference passes per-reference gates
Borderline rule:
- candidate is close to the top family
- candidate has strong support
- candidate did not satisfy full acceptance
Additional fallback acceptance rules exist for a few sparse-family cases in the current dataset. Those are there to preserve recall on the holdout scam fixtures while still keeping datasets/allowed safe.
The production matcher uses worker threads for the expensive visual path.
Main-thread behavior:
- raw SHA lookup always happens on the main thread
- if raw SHA matches, workers are bypassed
Worker-thread behavior:
- feature extraction
- archetype assignment
- family scoring
- classification
Notes:
- tests use the matcher with workers disabled for determinism
- the worker response is explicitly cloned into plain objects before posting back, to avoid runtime corruption of nested match-detail arrays
The pre-kick DM is rendered by src/templates.ts.
Behavior:
- uses a custom override if configured
- otherwise uses the default message from src/constants.ts
- replaces
{serverName}with the guild name - appends the configured rejoin invite URL if present
The repository uses three dataset groups:
datasets/scam- runtime-positive dataset
- used to build matcher fingerprints and family models
datasets/allowed- negative examples
- used for tests and offline calibration only
datasets/evaluate- holdout scam examples
- used in tests
Only datasets/scam is loaded at runtime.
- the archetype gate is currently aspect-ratio based, not semantic
- thresholds are heuristic and tied to the current dataset
- no OCR
- no general-purpose scam detection outside the known families
- worker threads are used in production, but tests primarily validate the main-thread path
This bot is best understood as a curated-image family matcher for a known scam campaign pattern. It is not trying to detect every scam screenshot on the internet. It is trying to reliably catch screenshots that visually belong to the scam families stored in datasets/scam, while avoiding false positives on unrelated dark-theme screenshots.
