zero-contact
v0.3.1
Published
Detect obfuscated phone numbers in text — numeric, word, emoji, and mixed formats
Maintainers
Readme
zero-contact
Detect obfuscated phone numbers in user-generated text. Built for moderation pipelines where contact sharing is restricted.
Supports numeric, word-spelled, emoji, homoglyph, and mixed obfuscation formats in Turkish, English, and Russian.
Install
npm install zero-contactQuick start
import { detect, detectMany, mask, quickCheck, replace } from 'zero-contact';
quickCheck('hello world'); // false
detect('0532 123 45 67');
// { detected: true, matches: [{ normalized: '05321234567', confidence: 0.9, ... }] }
detect('beş üç iki bir iki üç dört beş altı yedi');
// { detected: true, matches: [{ normalized: '5321234567', types: ['word'], ... }] }
mask('Arayın: 0532 123 45 67');
// 'Arayın: #### ### ## ##'
replace('Arayın: 0532 123 45 67');
// 'Arayın: [removed]'API
quickCheck(text, options?)
Fast pre-scan. Returns true when the text may contain a phone number. Use this as a cheap gate before detect().
detect(text, options?)
Full pipeline. Returns { detected, matches } with confidence scores and source ranges.
mask(text, options?)
Detects and masks phone-like sequences while preserving string length. Spaces are kept; other characters are replaced with # by default.
replace(text, options?)
Detects and replaces phone-like sequences with a custom string (default: [removed]).
detectMany(texts, options?)
Batch scan multiple texts. Uses quickCheck as a gate per item; only runs the full pipeline on positives.
Options
| Option | Default | Description |
|---|---|---|
| locales | [tr, en] | Locale dictionaries for word-digit matching |
| minDigits | 7 | Minimum consecutive digits to flag |
| minConfidence | 0.6 | Minimum confidence score (0–1) |
| countryHint | 'TR' | Scoring hint for national formats |
| firstMatch | false | Stop after the first qualifying match |
| maxMatches | undefined | Stop after this many qualifying matches |
| skipQuickCheck | false | Skip the internal pre-scan when you already ran quickCheck |
| char | '#' | Mask character (mask only) |
| replacement | '[removed]' | Replacement text (replace only) |
Moderation pipeline (early-exit)
Most user comments contain no phone signal. Use quickCheck as a cheap gate before the full pipeline:
import { quickCheck, detect } from 'zero-contact';
function scanComment(text: string) {
if (!quickCheck(text)) return null;
return detect(text, { skipQuickCheck: true });
}detect() already runs quickCheck internally. The pattern above avoids running the full pipeline when you only need a yes/no answer, and skipQuickCheck prevents scanning the text twice when you pre-filter with quickCheck.
For batch moderation, filter with quickCheck first and run detect or mask only on positives. Use firstMatch: true when you only need to know whether any number exists.
import { detectMany } from 'zero-contact';
const comments = ['hello', 'call 0532 123 45 67', 'nice product'];
const results = detectMany(comments);Tree-shakeable locales
Import only the locales you need:
import { detect } from 'zero-contact';
import { tr } from 'zero-contact/locales/tr';
import { ru } from 'zero-contact/locales/ru';
detect('beş üç iki bir iki üç dört beş altı yedi', { locales: [tr] });
detect('пять три два один два три четыре пять шесть семь', { locales: [ru] });What it detects
- Plain numbers:
0532 123 45 67,+90 532 123 45 67 - Unicode digits:
٠٥٣٢ ١٢٣ ٤٥ ٦٧ - Word spelling:
five three two...,beş üç iki... - Emoji keycaps:
5️⃣3️⃣2️⃣1️⃣2️⃣3️⃣4️⃣5️⃣6️⃣7️⃣ - Circled digits:
⑤③②①②③④⑤⑥⑦ - Homoglyphs:
O5³2 l23 4567 - Mixed obfuscation:
beş 5 üç iki bir... - Multipliers:
double five→55,çift bir→11 - Zero-width splits:
0532123(invisible characters between digits)
False-positive guards
Filters out common non-phone patterns:
- Dates:
01/02/2024 - Times:
14:30 - IPv4 addresses:
192.168.1.1 - IBAN numbers:
TR33 0006 1005... - Credit cards:
4532 1234 5678 9010 - Long reference IDs (18+ digits)
- Isolated digit words in prose:
bu ürün beş yıldız
Performance
Benchmarks on a typical dev machine:
| Scenario | ~Mean |
|---|---|
| quickCheck on clean 200-char comment | 0.017 ms |
| detect on obfuscated phone | 0.043 ms |
| detect on 2 KB text | 1.4 ms |
Zero runtime dependencies.
Development
npm install
npm test
npm run bench
npm run buildLicense
MIT — see package.json.
