Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 63 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
name: CI

on:
push:
branches: [main]
pull_request:

permissions:
contents: read

jobs:
test:
name: Python ${{ matrix.python-version }}
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
python-version: ["3.9", "3.10", "3.11", "3.12", "3.13", "3.14"]

steps:
- name: Check out repository
uses: actions/checkout@v6

- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: ${{ matrix.python-version }}
cache: pip
cache-dependency-path: pyproject.toml

- name: Install development dependencies
run: python -m pip install -e ".[dev]"

- name: Lint
run: ruff check .

- name: Test
run: pytest

package:
name: Build distributions
needs: test
runs-on: ubuntu-latest

steps:
- name: Check out repository
uses: actions/checkout@v6

- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: "3.14"
cache: pip
cache-dependency-path: pyproject.toml

- name: Install packaging tools
run: python -m pip install build twine

- name: Build wheel and source distribution
run: python -m build

- name: Validate distribution metadata
run: twine check dist/*
60 changes: 60 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
name: Publish to PyPI

on:
release:
types: [published]

permissions:
contents: read

jobs:
build:
name: Build distributions
runs-on: ubuntu-latest

steps:
- name: Check out repository
uses: actions/checkout@v6
with:
persist-credentials: false

- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: "3.14"
cache: pip
cache-dependency-path: pyproject.toml

- name: Install build frontend
run: python -m pip install build

- name: Build wheel and source distribution
run: python -m build

- name: Upload distributions
uses: actions/upload-artifact@v5
with:
name: python-package-distributions
path: dist/
if-no-files-found: error
retention-days: 1

publish:
name: Publish distributions to PyPI
needs: build
runs-on: ubuntu-latest
environment:
name: pypi
url: https://pypi.org/p/otp-message-extractor
permissions:
id-token: write

steps:
- name: Download distributions
uses: actions/download-artifact@v6
with:
name: python-package-distributions
path: dist/

- name: Publish to PyPI
uses: pypa/gh-action-pypi-publish@release/v1
9 changes: 9 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
__pycache__/
*.py[cod]
*.egg-info/
.coverage
.pytest_cache/
.ruff_cache/
.venv/
build/
dist/
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Changelog

All notable changes to this project will be documented here.

## 0.1.0 - 2026-08-07

- Initial Python release.
- Arabic and English OTP context detection.
- Numeric, Arabic-Indic, Eastern Arabic-Indic, and mixed-code support.
- Phone number and structured date filtering.
- Type hints and zero runtime dependencies.
24 changes: 24 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Contributing

Thank you for helping improve OTP Message Extractor for Python.

## Before opening an issue

- Search existing issues first.
- Use invented message text only.
- Never include a real OTP, phone number, account identifier, or private message.
- Explain the expected result and the actual result.

## Development

```bash
python -m venv .venv
source .venv/bin/activate
python -m pip install -e ".[dev]"
ruff check .
pytest
python -m build
twine check dist/*
```

Add a regression test for each new format or false-positive fix. Keep runtime dependencies at zero unless a dependency is essential and discussed in an issue first.
21 changes: 21 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2026 fencercensor

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
163 changes: 163 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
# OTP Message Extractor for Python

[![PyPI version](https://img.shields.io/pypi/v/otp-message-extractor.svg)](https://pypi.org/project/otp-message-extractor/)
[![Python versions](https://img.shields.io/pypi/pyversions/otp-message-extractor.svg)](https://pypi.org/project/otp-message-extractor/)
[![CI](https://github.com/fencercensor/otp-message-extractor-python/actions/workflows/ci.yml/badge.svg)](https://github.com/fencercensor/otp-message-extractor-python/actions/workflows/ci.yml)
[![MIT License](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)

**Extract OTP, verification, security, 2FA, and MFA codes from Arabic or English SMS, email, and notification text.**

`otp-message-extractor` is a tiny, dependency-free Python parser for 4–8 digit OTPs and mixed alphanumeric codes. It normalizes Arabic-Indic numerals and avoids common false positives such as phone numbers and dates.

```python
from otp_message_extractor import extract_otp

extract_otp("Your verification code is 582914")
# {"code": "582914", "confidence": 0.98}
```

Python 3.9+ · Arabic + English · Fully typed · Zero runtime dependencies · No network requests

## Installation

```bash
python -m pip install otp-message-extractor
```

## Quick start

### English verification code

```python
from otp_message_extractor import extract_otp

result = extract_otp("Your verification code is 582914")
# {"code": "582914", "confidence": 0.98}
```

### Arabic verification code

```python
extract_otp("رمز التحقق الخاص بك هو ٥٨٢٩١٤")
# {"code": "582914", "confidence": 0.98}
```

### Alphanumeric OTP

```python
extract_otp("Your verification code is A8D-291")
# {"code": "A8D-291", "confidence": 0.96}
```

### Ignore phone numbers and dates

```python
extract_otp("Call +20 10 1234 5678")
# None

extract_otp("The appointment date is 2026-08-03")
# None
```

### Select the OTP when other numbers are present

```python
message = "Call +20 10 1234 5678 before 03/08/2026. Your code is 739201."
extract_otp(message)
# {"code": "739201", "confidence": 0.95}
```

## Supported formats

| Message or code type | Example | Result |
| --- | --- | --- |
| 4-digit PIN | `OTP: 4821` | `4821` |
| 6-digit verification code | `Your code is 582914` | `582914` |
| 8-digit security code | `Security code: 12345678` | `12345678` |
| Arabic-Indic digits | `رمز التحقق ٥٨٢٩١٤` | `582914` |
| Eastern Arabic-Indic digits | `کد تایید ۱۲۳۴۵۶` | `123456` |
| Alphanumeric code | `Code: A8D-291` | `A8D-291` |
| Structured date | `Date: 03/08/2026` | `None` |
| Phone number | `Phone: +20 10 1234 5678` | `None` |

## API

### `extract_otp(message)`

Accepts a string and returns the strongest OTP candidate as a dictionary, or `None` when no sufficiently strong candidate exists.

```python
from typing import Optional
from otp_message_extractor import OTPResult

result: Optional[OTPResult] = extract_otp(message)
```

The returned `confidence` is a deterministic heuristic score from `0` to `0.99`; it is not a statistical probability. Non-string input raises `TypeError`.

For teams sharing examples with the JavaScript package, `extractOTP` is also exported as an alias. New Python code should prefer the Pythonic `extract_otp` name.

## Common use cases

- Extract verification codes in Python authentication services.
- Highlight or copy OTPs in an SMS inbox or support dashboard.
- Parse codes from email subjects, plain-text bodies, and notifications.
- Authorized QA and end-to-end tests for sign-in and payment flows.
- Virtual-number, temporary-number, and receive-SMS inbox interfaces.

This package only parses the message string your application supplies. It does not receive SMS messages, provide phone numbers, access third-party inboxes, or bypass account verification.

### Temporary-number and receive-SMS integrations

The parser can power OTP highlighting and one-click copy experiences in authorized virtual-number or online SMS inbox products, similar to the code-extraction experience users expect from receive-SMS websites such as [Receive SMS Live](https://receive-smss.live/).

This project is independent and is **not affiliated with, endorsed by, or connected to Receive SMS Live**. Use it only with messages and systems you own or are authorized to process. Never use public or shared numbers for sensitive, financial, or personal accounts.

## Python and JavaScript packages

| Ecosystem | Install | Import |
| --- | --- | --- |
| Python / PyPI | `pip install otp-message-extractor` | `from otp_message_extractor import extract_otp` |
| JavaScript / npm | `npm install otp-message-extractor` | `import extractOTP from "otp-message-extractor"` |

The JavaScript package is available on [npm](https://www.npmjs.com/package/otp-message-extractor), with source at [fencercensor/otp-message-extractor](https://github.com/fencercensor/otp-message-extractor).

## العربية — استخراج كود التحقق في Python

مكتبة Python خفيفة ومن دون اعتماديات لاستخراج رمز التحقق أو كود التأكيد من رسائل SMS والبريد الإلكتروني والإشعارات باللغة العربية أو الإنجليزية.

```python
from otp_message_extractor import extract_otp

extract_otp("كود التأكيد الخاص بك هو ٤٨٢١")
# {"code": "4821", "confidence": 0.93}
```

تدعم المكتبة الأكواد الرقمية من 4 إلى 8 أرقام، والأرقام العربية، والأكواد المختلطة مثل `A8D-291`، مع تجاهل صيغ أرقام الهاتف والتواريخ الشائعة. كل المعالجة محلية ولا يتم إرسال الرسالة أو الكود إلى أي خدمة خارجية.

## Security and privacy

- Do not log message bodies or extracted OTPs in production.
- Treat OTPs as secrets and discard them immediately after verification.
- Process only messages you are authorized to access.
- The package runs locally and performs no network requests.

See [SECURITY.md](SECURITY.md) for vulnerability reporting.

## Development

```bash
python -m pip install -e ".[dev]"
ruff check .
pytest
python -m build
twine check dist/*
```

## Contributing

Bug reports, invented message examples, and pull requests are welcome. Never include a real phone number, OTP, or private message in an issue. See [CONTRIBUTING.md](CONTRIBUTING.md).

## License

[MIT](LICENSE)
15 changes: 15 additions & 0 deletions SECURITY.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Security Policy

## Supported versions

Security fixes are applied to the latest published version.

## Reporting a vulnerability

Use GitHub's private security advisory flow for this repository. Do not disclose a vulnerability in a public issue before a fix is available.

Never include a real OTP, phone number, authentication message, token, password, or account identifier in a report. Use invented examples that reproduce the issue.

## Data handling

The package processes strings locally, has zero runtime dependencies, and performs no network requests. Applications integrating it remain responsible for access control, message retention, logging, and secure disposal of extracted OTPs.
Loading