turnley.dev

PHP and MySQL code for faucet operators

Tools & Calculators

FRD-010

An admin view that shows you who is farming you

Four queries — earnings concentration, shared visitor keys, signup velocity, payout-to-activity ratio — that surface abuse without a fraud engine.

Most abuse on a small site is visible in aggregate long before any individual event looks wrong. One account earning three times the median is not proof of anything, and it is exactly where to look first.

These four queries answer the questions worth asking daily. Who is earning far more than everyone else. Which visitor keys are shared across accounts. Did a burst of accounts appear at once. Is anyone withdrawing far more than their recorded activity would produce.

Put them on one admin page and read it while your coffee brews. That is a better fraud system than most sites this size ever build.

PHP
// 1. Earnings concentration — top earners against the median.
$top = $pdo->query(
    'SELECT user_id, SUM(units) AS earned, COUNT(*) AS events
     FROM ledger
     WHERE kind IN ("claim","mining","ptc") AND created_at >= DATE_SUB(NOW(), INTERVAL 7 DAY)
     GROUP BY user_id ORDER BY earned DESC LIMIT 20'
)->fetchAll(PDO::FETCH_ASSOC);

// 2. One visitor key, several accounts — the clearest multi-account signal.
$shared = $pdo->query(
    'SELECT visitor_key, COUNT(DISTINCT user_id) AS accounts, GROUP_CONCAT(DISTINCT user_id) AS ids
     FROM activity
     WHERE created_at >= DATE_SUB(NOW(), INTERVAL 7 DAY)
     GROUP BY visitor_key HAVING accounts > 2 ORDER BY accounts DESC LIMIT 20'
)->fetchAll(PDO::FETCH_ASSOC);

// 3. Signup velocity — bursts stand out against a normal day.
$signups = $pdo->query(
    'SELECT DATE_FORMAT(created_at, "%Y-%m-%d %H:00") AS hour, COUNT(*) AS n
     FROM users WHERE created_at >= DATE_SUB(NOW(), INTERVAL 3 DAY)
     GROUP BY hour ORDER BY n DESC LIMIT 10'
)->fetchAll(PDO::FETCH_ASSOC);

// 4. Withdrawn far more than the activity log accounts for.
$mismatch = $pdo->query(
    'SELECT u.id, u.username,
            COALESCE(SUM(w.units), 0) AS withdrawn,
            (SELECT COUNT(*) FROM activity a WHERE a.user_id = u.id) AS actions
     FROM users u LEFT JOIN withdrawals w ON w.user_id = u.id AND w.status = "paid"
     GROUP BY u.id
     HAVING withdrawn > 0 AND actions < 20
     ORDER BY withdrawn DESC LIMIT 20'
)->fetchAll(PDO::FETCH_ASSOC);

Using it

Link every row to the member record so a suspicious line is one click from the full history.

Shadow-ban rather than delete. An account that keeps working and earns nothing tells you what the operator was trying to do; a deleted one just comes back.

What bites people

A shared visitor key means a shared network, which includes families, offices and phone carriers. Investigate, do not act automatically.

Concentration alone is not fraud. Your most active honest user will always be near the top of that list.

This one touches real money. Point it at a throwaway wallet and watch a full cycle before you trust it with a live balance.

Also in Anti-Bot and Fraud