Skip to content

Commit 2dd6b98

Browse files
authored
feat: add Python OTP extractor
Validated across Python 3.9–3.14, package build, metadata checks, parity cases, and clean wheel installation.
1 parent d9c7eb8 commit 2dd6b98

13 files changed

Lines changed: 836 additions & 0 deletions

File tree

.github/workflows/ci.yml

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
branches: [main]
6+
pull_request:
7+
8+
permissions:
9+
contents: read
10+
11+
jobs:
12+
test:
13+
name: Python ${{ matrix.python-version }}
14+
runs-on: ubuntu-latest
15+
strategy:
16+
fail-fast: false
17+
matrix:
18+
python-version: ["3.9", "3.10", "3.11", "3.12", "3.13", "3.14"]
19+
20+
steps:
21+
- name: Check out repository
22+
uses: actions/checkout@v6
23+
24+
- name: Set up Python
25+
uses: actions/setup-python@v6
26+
with:
27+
python-version: ${{ matrix.python-version }}
28+
cache: pip
29+
cache-dependency-path: pyproject.toml
30+
31+
- name: Install development dependencies
32+
run: python -m pip install -e ".[dev]"
33+
34+
- name: Lint
35+
run: ruff check .
36+
37+
- name: Test
38+
run: pytest
39+
40+
package:
41+
name: Build distributions
42+
needs: test
43+
runs-on: ubuntu-latest
44+
45+
steps:
46+
- name: Check out repository
47+
uses: actions/checkout@v6
48+
49+
- name: Set up Python
50+
uses: actions/setup-python@v6
51+
with:
52+
python-version: "3.14"
53+
cache: pip
54+
cache-dependency-path: pyproject.toml
55+
56+
- name: Install packaging tools
57+
run: python -m pip install build twine
58+
59+
- name: Build wheel and source distribution
60+
run: python -m build
61+
62+
- name: Validate distribution metadata
63+
run: twine check dist/*

.github/workflows/release.yml

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
name: Publish to PyPI
2+
3+
on:
4+
release:
5+
types: [published]
6+
7+
permissions:
8+
contents: read
9+
10+
jobs:
11+
build:
12+
name: Build distributions
13+
runs-on: ubuntu-latest
14+
15+
steps:
16+
- name: Check out repository
17+
uses: actions/checkout@v6
18+
with:
19+
persist-credentials: false
20+
21+
- name: Set up Python
22+
uses: actions/setup-python@v6
23+
with:
24+
python-version: "3.14"
25+
cache: pip
26+
cache-dependency-path: pyproject.toml
27+
28+
- name: Install build frontend
29+
run: python -m pip install build
30+
31+
- name: Build wheel and source distribution
32+
run: python -m build
33+
34+
- name: Upload distributions
35+
uses: actions/upload-artifact@v5
36+
with:
37+
name: python-package-distributions
38+
path: dist/
39+
if-no-files-found: error
40+
retention-days: 1
41+
42+
publish:
43+
name: Publish distributions to PyPI
44+
needs: build
45+
runs-on: ubuntu-latest
46+
environment:
47+
name: pypi
48+
url: https://pypi.org/p/otp-message-extractor
49+
permissions:
50+
id-token: write
51+
52+
steps:
53+
- name: Download distributions
54+
uses: actions/download-artifact@v6
55+
with:
56+
name: python-package-distributions
57+
path: dist/
58+
59+
- name: Publish to PyPI
60+
uses: pypa/gh-action-pypi-publish@release/v1

.gitignore

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
__pycache__/
2+
*.py[cod]
3+
*.egg-info/
4+
.coverage
5+
.pytest_cache/
6+
.ruff_cache/
7+
.venv/
8+
build/
9+
dist/

CHANGELOG.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# Changelog
2+
3+
All notable changes to this project will be documented here.
4+
5+
## 0.1.0 - 2026-08-07
6+
7+
- Initial Python release.
8+
- Arabic and English OTP context detection.
9+
- Numeric, Arabic-Indic, Eastern Arabic-Indic, and mixed-code support.
10+
- Phone number and structured date filtering.
11+
- Type hints and zero runtime dependencies.

CONTRIBUTING.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# Contributing
2+
3+
Thank you for helping improve OTP Message Extractor for Python.
4+
5+
## Before opening an issue
6+
7+
- Search existing issues first.
8+
- Use invented message text only.
9+
- Never include a real OTP, phone number, account identifier, or private message.
10+
- Explain the expected result and the actual result.
11+
12+
## Development
13+
14+
```bash
15+
python -m venv .venv
16+
source .venv/bin/activate
17+
python -m pip install -e ".[dev]"
18+
ruff check .
19+
pytest
20+
python -m build
21+
twine check dist/*
22+
```
23+
24+
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.

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2026 fencercensor
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

README.md

Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
# OTP Message Extractor for Python
2+
3+
[![PyPI version](https://img.shields.io/pypi/v/otp-message-extractor.svg)](https://pypi.org/project/otp-message-extractor/)
4+
[![Python versions](https://img.shields.io/pypi/pyversions/otp-message-extractor.svg)](https://pypi.org/project/otp-message-extractor/)
5+
[![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)
6+
[![MIT License](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
7+
8+
**Extract OTP, verification, security, 2FA, and MFA codes from Arabic or English SMS, email, and notification text.**
9+
10+
`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.
11+
12+
```python
13+
from otp_message_extractor import extract_otp
14+
15+
extract_otp("Your verification code is 582914")
16+
# {"code": "582914", "confidence": 0.98}
17+
```
18+
19+
Python 3.9+ · Arabic + English · Fully typed · Zero runtime dependencies · No network requests
20+
21+
## Installation
22+
23+
```bash
24+
python -m pip install otp-message-extractor
25+
```
26+
27+
## Quick start
28+
29+
### English verification code
30+
31+
```python
32+
from otp_message_extractor import extract_otp
33+
34+
result = extract_otp("Your verification code is 582914")
35+
# {"code": "582914", "confidence": 0.98}
36+
```
37+
38+
### Arabic verification code
39+
40+
```python
41+
extract_otp("رمز التحقق الخاص بك هو ٥٨٢٩١٤")
42+
# {"code": "582914", "confidence": 0.98}
43+
```
44+
45+
### Alphanumeric OTP
46+
47+
```python
48+
extract_otp("Your verification code is A8D-291")
49+
# {"code": "A8D-291", "confidence": 0.96}
50+
```
51+
52+
### Ignore phone numbers and dates
53+
54+
```python
55+
extract_otp("Call +20 10 1234 5678")
56+
# None
57+
58+
extract_otp("The appointment date is 2026-08-03")
59+
# None
60+
```
61+
62+
### Select the OTP when other numbers are present
63+
64+
```python
65+
message = "Call +20 10 1234 5678 before 03/08/2026. Your code is 739201."
66+
extract_otp(message)
67+
# {"code": "739201", "confidence": 0.95}
68+
```
69+
70+
## Supported formats
71+
72+
| Message or code type | Example | Result |
73+
| --- | --- | --- |
74+
| 4-digit PIN | `OTP: 4821` | `4821` |
75+
| 6-digit verification code | `Your code is 582914` | `582914` |
76+
| 8-digit security code | `Security code: 12345678` | `12345678` |
77+
| Arabic-Indic digits | `رمز التحقق ٥٨٢٩١٤` | `582914` |
78+
| Eastern Arabic-Indic digits | `کد تایید ۱۲۳۴۵۶` | `123456` |
79+
| Alphanumeric code | `Code: A8D-291` | `A8D-291` |
80+
| Structured date | `Date: 03/08/2026` | `None` |
81+
| Phone number | `Phone: +20 10 1234 5678` | `None` |
82+
83+
## API
84+
85+
### `extract_otp(message)`
86+
87+
Accepts a string and returns the strongest OTP candidate as a dictionary, or `None` when no sufficiently strong candidate exists.
88+
89+
```python
90+
from typing import Optional
91+
from otp_message_extractor import OTPResult
92+
93+
result: Optional[OTPResult] = extract_otp(message)
94+
```
95+
96+
The returned `confidence` is a deterministic heuristic score from `0` to `0.99`; it is not a statistical probability. Non-string input raises `TypeError`.
97+
98+
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.
99+
100+
## Common use cases
101+
102+
- Extract verification codes in Python authentication services.
103+
- Highlight or copy OTPs in an SMS inbox or support dashboard.
104+
- Parse codes from email subjects, plain-text bodies, and notifications.
105+
- Authorized QA and end-to-end tests for sign-in and payment flows.
106+
- Virtual-number, temporary-number, and receive-SMS inbox interfaces.
107+
108+
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.
109+
110+
### Temporary-number and receive-SMS integrations
111+
112+
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/).
113+
114+
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.
115+
116+
## Python and JavaScript packages
117+
118+
| Ecosystem | Install | Import |
119+
| --- | --- | --- |
120+
| Python / PyPI | `pip install otp-message-extractor` | `from otp_message_extractor import extract_otp` |
121+
| JavaScript / npm | `npm install otp-message-extractor` | `import extractOTP from "otp-message-extractor"` |
122+
123+
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).
124+
125+
## العربية — استخراج كود التحقق في Python
126+
127+
مكتبة Python خفيفة ومن دون اعتماديات لاستخراج رمز التحقق أو كود التأكيد من رسائل SMS والبريد الإلكتروني والإشعارات باللغة العربية أو الإنجليزية.
128+
129+
```python
130+
from otp_message_extractor import extract_otp
131+
132+
extract_otp("كود التأكيد الخاص بك هو ٤٨٢١")
133+
# {"code": "4821", "confidence": 0.93}
134+
```
135+
136+
تدعم المكتبة الأكواد الرقمية من 4 إلى 8 أرقام، والأرقام العربية، والأكواد المختلطة مثل `A8D-291`، مع تجاهل صيغ أرقام الهاتف والتواريخ الشائعة. كل المعالجة محلية ولا يتم إرسال الرسالة أو الكود إلى أي خدمة خارجية.
137+
138+
## Security and privacy
139+
140+
- Do not log message bodies or extracted OTPs in production.
141+
- Treat OTPs as secrets and discard them immediately after verification.
142+
- Process only messages you are authorized to access.
143+
- The package runs locally and performs no network requests.
144+
145+
See [SECURITY.md](SECURITY.md) for vulnerability reporting.
146+
147+
## Development
148+
149+
```bash
150+
python -m pip install -e ".[dev]"
151+
ruff check .
152+
pytest
153+
python -m build
154+
twine check dist/*
155+
```
156+
157+
## Contributing
158+
159+
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).
160+
161+
## License
162+
163+
[MIT](LICENSE)

SECURITY.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# Security Policy
2+
3+
## Supported versions
4+
5+
Security fixes are applied to the latest published version.
6+
7+
## Reporting a vulnerability
8+
9+
Use GitHub's private security advisory flow for this repository. Do not disclose a vulnerability in a public issue before a fix is available.
10+
11+
Never include a real OTP, phone number, authentication message, token, password, or account identifier in a report. Use invented examples that reproduce the issue.
12+
13+
## Data handling
14+
15+
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.

0 commit comments

Comments
 (0)