diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..561c0be --- /dev/null +++ b/.editorconfig @@ -0,0 +1,14 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true + +[*.md] +trim_trailing_whitespace = false + +[*.{js,php,py}] +indent_style = space +indent_size = 4 diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md new file mode 100644 index 0000000..6aa8006 --- /dev/null +++ b/.github/CONTRIBUTING.md @@ -0,0 +1,43 @@ +# Contributing to CreationDate + +Thank you for your interest in contributing to CreationDate! + +Contributions from the community help improve this project and make it more useful for everyone. + +## How to Contribute + +### 1. Reporting Bugs +* Check the [Issues](https://github.com/wizardloop/CreationDate/issues) to see if the bug has already been reported. +* If not, open a new issue with a clear description of the problem and steps to reproduce it. + +### 2. Suggesting Features +* Open an issue with your feature request. +* Include a clear description and, if possible, examples of how it would work. + +### 3. Submitting Pull Requests +* Fork the repository and clone it to your local machine. +* Create a new branch for your changes: `git checkout -b feature-name`. +* Make your changes with clear commit messages. +* Test your changes locally. +* Push your branch and submit a Pull Request (PR) to the `main` branch. + +### 4. Adding UserID Points +* Update `data/tg_points.json` with additional Telegram UserID points. +* Ensure the JSON format remains valid. + +### 5. Improving Documentation +* Suggestions or corrections to the README or other documentation are welcome. +* Update examples, formatting, or clarity. + +### 6. Code Style +* Keep code clean and readable. +* Use consistent indentation and naming conventions. +* Add comments where necessary. + +### 7. License and Code of Conduct +* By contributing, you agree that your contributions will be licensed under the [MIT License](https://github.com/wizardloop/CreationDate/LICENSE). +* Follow the [Code of Conduct](https://github.com/wizardloop/CreationDate/.github/CODE_OF_CONDUCT.md) when interacting with the community. + +--- + +We appreciate all contributions, big or small! Thank you for helping improve CreationDate. diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 0000000..897f3f2 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,27 @@ +name: Bug Report 🐞 +description: Report a bug or issue. +title: "[Bug] " +labels: [bug] +body: + - type: textarea + id: bug + attributes: + label: What happened? + description: Clearly describe the problem and what you expected to happen. + validations: + required: true + - type: textarea + id: steps + attributes: + label: Steps to Reproduce + description: List the exact steps to reproduce this bug. + - type: input + id: env + attributes: + label: Environment + description: PHP version and OS. + - type: textarea + id: logs + attributes: + label: Relevant logs/output + description: Include any output or error logs if relevant. diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml new file mode 100644 index 0000000..611b3fb --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -0,0 +1,18 @@ +name: Feature Request ✨ +description: Suggest a magical new feature! +title: "[Feature] " +labels: [enhancement, feature] +body: + - type: textarea + id: description + attributes: + label: Feature Description + description: Please describe the new feature you'd like to see. + validations: + required: true + - type: textarea + id: example + attributes: + label: Usage Example + description: Show a code example or output illustrating how this feature would work. + diff --git a/.github/ISSUE_TEMPLATE/improvement_question.yml b/.github/ISSUE_TEMPLATE/improvement_question.yml new file mode 100644 index 0000000..c01c501 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/improvement_question.yml @@ -0,0 +1,18 @@ +name: Improvement / Question πŸ€” +description: Suggest an improvement or ask a question. +title: "[Q] " +labels: [question, improvement] +body: + - type: textarea + id: description + attributes: + label: Description / Question + description: Clearly describe your suggestion or question. + validations: + required: true + - type: checkboxes + id: pr + attributes: + label: Are you planning to submit a PR? + options: + - label: Yes, I will submit a PR with this improvement/answer. diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..2cd5e58 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,21 @@ +## Description + +Please include a summary of the changes and the related issue. +Please also include relevant motivation and context. + +Fixes # (issue) + +## Type of Change + +- [ ] Bug fix +- [ ] New feature +- [ ] Documentation update +- [ ] Code refactor +- [ ] Other (please describe): + +## Checklist + +- [ ] My code follows the project’s code style +- [ ] I have tested my changes locally +- [ ] I have added necessary documentation +- [ ] I have updated CHANGELOG.md if applicable diff --git a/.github/SECURITY.md b/.github/SECURITY.md new file mode 100644 index 0000000..7199779 --- /dev/null +++ b/.github/SECURITY.md @@ -0,0 +1,9 @@ +# Security Policy + +## Reporting a Vulnerability +If you discover a security vulnerability in CreationDate, please report it by contacting [wizardloop.t.me] immediately. + +Do not open a public issue with sensitive security information. + +## Supported Versions +All released versions are considered supported unless otherwise noted. diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0eef7fb --- /dev/null +++ b/.gitignore @@ -0,0 +1,40 @@ +# Node / JavaScript +node_modules/ +dist/ +npm-debug.log +yarn-error.log +package-lock.json +yarn.lock + +# Python +__pycache__/ +*.pyc +*.pyo +*.pyd + +# PHP +vendor/ +*.log + +# Logs +*.log + +# OS +.DS_Store +Thumbs.db +Desktop.ini + +# IDE +.vscode/ +.idea/ + +# Temp files +*.tmp +*.swp + +# GitHub Pages / Build cache +.sass-cache/ +.cache/ + +# Ignore tg_points.json if you don’t want to push updates automatically +# data/tg_points.json diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..3e8ddaf --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,37 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +--- + +## [1.0.0] - 2026-01-25 + +### Added +- Initial release of **CreationDate** +- Browser-based API to estimate Telegram user creation dates from UserID +- JSON dataset of Telegram accounts (`data/tg_points.json`) +- Core estimation algorithm implemented in: + - PHP (`examples/php/estimate.php`) + - Python (`examples/python/estimate.py`) +- Static API interface (`api/index.html` + `api.js`) +- Documentation (`README.md`, `data/README.md`) +- Schema file for dataset (`data/schema.json`) +- Contribution guidelines (`.github/CONTRIBUTING.md`) +- Code of Conduct (`.github/CODE_OF_CONDUCT.md`) +- Security policy (`.github/SECURITY.md`) +- GitHub issue templates: + - Bug report + - Feature request + - Improvement / question +- Pull Request template +- `.gitignore` configured for multiple environments (Node, Python, logs, OS, IDE) + +### Changed +- N/A (first release) + +### Fixed +- N/A (first release) + +--- + + diff --git a/README.md b/README.md index 1567dcb..dc0084e 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,257 @@ # CreationDate -CreationDate is an open-source, lightweight project that estimates the creation date of Telegram account. + +[![Accuracy](https://img.shields.io/badge/accuracy-weeks_to_month-green)](#accuracy--limitations) +[![Platform](https://img.shields.io/badge/platform-browser%20%7C%20static-lightgrey)](#api-usage) +[![Status](https://img.shields.io/badge/status-stable-brightgreen)](#) + +[![GitHub issues](https://img.shields.io/github/issues/wizardloop/CreationDate)](https://github.com/wizardloop/CreationDate/issues) +[![License](https://img.shields.io/github/license/wizardloop/CreationDate)](LICENSE) +[![GitHub stars](https://img.shields.io/github/stars/wizardloop/CreationDate?style=social)](https://github.com/wizardloop/CreationDate) +[![GitHub forks](https://img.shields.io/github/forks/wizardloop/CreationDate?style=social)](https://github.com/wizardloop/CreationDate) + +--- + +## Overview + +**CreationDate** is a lightweight, browser-based API and dataset for estimating +the **creation date of Telegram private user accounts** using only their numeric **UserID**. + +The project is based on a curated dataset of Telegram accounts with **known creation dates**, +collected over several years, and applies **linear interpolation** to estimate unknown accounts. + +No Telegram API, no authentication, no backend, no rate limits. + +--- + +## Features + +- Estimate Telegram account creation date from UserID +- Returns exact date (`YYYY-MM-DD`) and human-readable account age +- Fully client-side (static files only) +- Reusable core algorithm (PHP / Python examples included) +- Easy GitHub Pages deployment +- Transparent and auditable dataset + +--- + +⚠️ **Notice** +This project provides **estimates only**, based on publicly observed UserID growth patterns. + +--- + +## How It Works + +Telegram assigns numeric UserIDs in roughly ascending order over time. +CreationDate uses a reference dataset of known `(UserID β†’ creation date)` points. + +For a given UserID, the algorithm: + +1. Sorts known UserID points +2. Finds the surrounding range +3. Applies linear interpolation between timestamps +4. Prevents future-dated results + +--- + +## API Usage + +The API is fully static and browser-based. + +### Endpoint + +`/api/?userid=TELEGRAM_USER_ID` + +### Example + +https://wizardloop.github.io/CreationDate/api/?userid=208238248 + +### Successful Response + +```json +{ + "userid": 208238248, + "creation_date": "2016-04-10", + "account_age": "8 years" +} +``` + +### Error Response + +```json +{ + "error": "Invalid userid" +} +``` + +--- + +## Using the API from Code + +### JavaScript (Browser) + +```javascript +fetch("https://wizardloop.github.io/CreationDate/api/?userid=208238248") + .then(res => res.json()) + .then(data => console.log(data)); +``` + +### PHP + +```php +a[0]-b[0]); + if(userId <= points[0][0]) return new Date(points[0][1]); + + for(let i=0;i= id1 && userId <= id2) return interpolate(userId,id1,date1,id2,date2); + } + + const [id1,date1] = points[points.length-2], [id2,date2] = points[points.length-1]; + return interpolate(userId,id1,date1,id2,date2); +} + +function interpolate(userId,id1,date1,id2,date2){ + if(id1===id2) return new Date(date1); + const t1 = new Date(date1).getTime(), t2 = new Date(date2).getTime(); + const ts = t1 + (t2-t1)*((userId-id1)/(id2-id1)); + const result = new Date(ts); + return result > new Date() ? new Date() : result; +} + +function getAccountAgeString(createdDate){ + const now = new Date(); + let years = now.getFullYear() - createdDate.getFullYear(); + let months = now.getMonth() - createdDate.getMonth(); + if(months < 0){ years--; months += 12; } + if(years >= 1) return `${years} ${years===1?'year':'years'}`; + if(months >= 1) return `${months} ${months===1?'month':'months'}`; + return 'less than a month'; +} + +if(!useridStr || isNaN(useridStr)){ + sendJSON({error:"Invalid userid"}); +} else if(useridStr.startsWith('-')){ + sendJSON({error:"Invalid userid (cannot start with -)"}); +} else { + const userid = parseInt(useridStr,10); + fetch('../data/tg_points.json') + .then(res => res.json()) + .then(points => { + const creationDate = estimateTelegramCreationDate(userid, points); + sendJSON({ + userid, + creation_date: creationDate.toISOString().split('T')[0], + account_age: getAccountAgeString(creationDate) + }); + }) + .catch(err => { + console.error(err); + sendJSON({error:"Cannot load tg_points.json"}); + }); +} diff --git a/api/index.html b/api/index.html new file mode 100644 index 0000000..e8affd5 --- /dev/null +++ b/api/index.html @@ -0,0 +1,12 @@ + + + + +Creation Date API + + + +

+
+
+
diff --git a/data/README.md b/data/README.md
new file mode 100644
index 0000000..29c119d
--- /dev/null
+++ b/data/README.md
@@ -0,0 +1,119 @@
+# Telegram UserID Reference Dataset
+
+This directory contains the core dataset used by **CreationDate**
+to estimate Telegram account creation dates.
+
+The main file is:
+
+- `tg_points.json`
+
+It serves as a reference map between Telegram **private user IDs**
+and their known or strongly verified creation periods.
+
+---
+
+## Data Origin
+
+The dataset was collected and refined over several years through
+long-term observation.
+
+It is based on **real private Telegram user accounts** whose creation
+periods are known or can be reliably inferred.
+
+No private Telegram APIs, leaked databases, or automated scraping
+methods were used.
+
+---
+
+## What This Dataset Includes
+
+- βœ… Private Telegram user accounts only
+- ❌ No bots
+- ❌ No groups
+- ❌ No channels
+- ❌ No service or system accounts
+
+UserIDs belonging to groups or channels  
+(e.g. IDs starting with `-100...`) are intentionally excluded.
+
+---
+
+## Accuracy Characteristics
+
+- **Year accuracy:** very high
+- **Month accuracy:** very high
+- **Typical deviation:** weeks
+- **Older accounts:** slightly wider margin, still reliable on a monthly level
+
+This dataset is optimized for **temporal accuracy**, not completeness.
+
+---
+
+## Data Format
+
+Each entry in `tg_points.json` follows this structure:
+
+```json
+[user_id, "YYYY-MM-DD"]
+```
+
+Example:
+
+```json
+[2768409, "2013-11-01"]
+```
+
+Where:
+
+- `user_id` is a Telegram private user ID
+- `YYYY-MM-DD` represents the account creation date or period anchor
+
+The file must remain:
+- Valid JSON
+- Chronologically consistent
+- Sorted or sortable by UserID
+
+---
+
+## How It Is Used
+
+These reference points act as **anchor nodes**.
+
+CreationDate estimates unknown UserIDs by applying
+**linear interpolation** between the closest known points.
+
+Accuracy improves as:
+- More anchor points are added
+- Coverage across different years increases
+
+---
+
+## Limitations
+
+- The dataset does **not** represent all Telegram users
+- Dates are **estimates**, not official Telegram data
+- Precision depends on surrounding anchor density
+
+---
+
+## Contributing Data
+
+If you wish to contribute:
+
+- Only include **private user accounts**
+- Exclude bots, groups, and channels
+- Ensure dates are reasonable and verifiable
+- Maintain chronological consistency
+
+Please follow the guidelines in:
+
+`.github/CONTRIBUTING.md`
+
+---
+
+## Legal & Ethical Notice
+
+This dataset does **not** contain personal data beyond numeric UserIDs
+and estimated creation periods.
+
+It is intended for research, development, and educational purposes only.
diff --git a/data/schema.json b/data/schema.json
new file mode 100644
index 0000000..6909549
--- /dev/null
+++ b/data/schema.json
@@ -0,0 +1,28 @@
+{
+  "$schema": "https://json-schema.org/draft/2020-12/schema",
+  "title": "Telegram UserID Reference Point",
+  "description": "Schema for Telegram private user ID anchor points used for creation date estimation.",
+  "type": "array",
+  "items": {
+    "type": "array",
+    "minItems": 2,
+    "maxItems": 2,
+    "items": [
+      {
+        "type": "integer",
+        "description": "Telegram private user ID",
+        "minimum": 1
+      },
+      {
+        "type": "string",
+        "description": "Observed account creation date (YYYY-MM-DD)",
+        "pattern": "^\\d{4}-\\d{2}-\\d{2}$"
+      }
+    ]
+  },
+  "examples": [
+    [2768409, "2013-11-01"],
+    [805158066, "2019-07-15"],
+    [6926984452, "2024-01-09"]
+  ]
+}
diff --git a/data/tg_points.json b/data/tg_points.json
new file mode 100644
index 0000000..19e1690
--- /dev/null
+++ b/data/tg_points.json
@@ -0,0 +1,214 @@
+[
+  [0, "2013-08-14"],
+  [2768409, "2013-11-01"],
+  [7679610, "2013-12-31"],
+  [11538514, "2014-02-01"],
+  [15835244, "2014-02-20"],
+  [23646077, "2014-02-26"],
+  [38015510, "2014-03-01"],
+  [44634663, "2014-05-06"],
+  [46145305, "2014-05-15"],
+  [54845238, "2014-09-20"],
+  [63263518, "2014-10-27"],
+  [101260938, "2015-03-06"],
+  [101323197, "2015-03-13"],
+  [111220210, "2015-04-21"],
+  [103258382, "2015-05-27"],
+  [103151531, "2015-06-03"],
+  [116812045, "2015-07-23"],
+  [122600695, "2015-07-24"],
+  [109393468, "2015-08-08"],
+  [112594714, "2015-08-15"],
+  [124872445, "2015-08-17"],
+  [130029930, "2015-09-03"],
+  [125828524, "2015-10-05"],
+  [133909606, "2015-10-07"],
+  [157242073, "2015-11-06"],
+  [143445125, "2015-12-01"],
+  [148670295, "2016-01-08"],
+  [152079341, "2016-01-22"],
+  [171295414, "2016-03-09"],
+  [181783990, "2016-04-10"],
+  [222021233, "2016-06-08"],
+  [225034354, "2016-06-18"],
+  [278941742, "2016-09-10"],
+  [285253072, "2016-10-18"],
+  [294851037, "2016-11-19"],
+  [297621225, "2016-12-16"],
+  [328594461, "2017-01-28"],
+  [337808429, "2017-02-21"],
+  [341546272, "2017-02-22"],
+  [352940995, "2017-02-24"],
+  [369669043, "2017-03-31"],
+  [400169472, "2017-07-31"],
+  [805158066, "2019-07-15"],
+  [1974255900, "2021-10-12"],
+  [5031711230, "2021-12-06"],
+  [5022636255, "2021-12-10"],
+  [5045293264, "2022-01-13"],
+  [5070164216, "2022-01-19"],
+  [5149590651, "2022-01-22"],
+  [5177789190, "2022-01-24"],
+  [5288930461, "2022-01-27"],
+  [5207110227, "2022-02-03"],
+  [5210565134, "2022-02-09"],
+  [5196353812, "2022-02-15"],
+  [5124771193, "2022-02-22"],
+  [5170390109, "2022-02-28"],
+  [5169485538, "2022-03-01"],
+  [5260388619, "2022-03-02"],
+  [5106451106, "2022-03-05"],
+  [5179102906, "2022-03-12"],
+  [5155903109, "2022-03-22"],
+  [5268253519, "2022-03-22"],
+  [5153900870, "2022-03-22"],
+  [5047148663, "2022-04-05"],
+  [5144324763, "2022-04-06"],
+  [5244529493, "2022-04-19"],
+  [5396515972, "2022-04-21"],
+  [5308260177, "2022-04-25"],
+  [5349830748, "2022-04-29"],
+  [5271530336, "2022-04-30"],
+  [5259159476, "2022-05-06"],
+  [5166844465, "2022-05-09"],
+  [5394432429, "2022-05-23"],
+  [5351497367, "2022-05-24"],
+  [5505809357, "2022-05-27"],
+  [5433708969, "2022-05-28"],
+  [5488407539, "2022-05-29"],
+  [5598262640, "2022-06-11"],
+  [5596032583, "2022-06-16"],
+  [5159326926, "2022-06-16"],
+  [5434011049, "2022-06-29"],
+  [5542245357, "2022-07-07"],
+  [5428357996, "2022-07-11"],
+  [5468950164, "2022-07-14"],
+  [5468433192, "2022-07-20"],
+  [5442755368, "2022-07-23"],
+  [5546930145, "2022-07-24"],
+  [5363536419, "2022-08-01"],
+  [5451256696, "2022-08-06"],
+  [5519218712, "2022-08-14"],
+  [5694365966, "2022-08-28"],
+  [5721138769, "2022-09-23"],
+  [5601951167, "2022-10-02"],
+  [5515826405, "2022-10-03"],
+  [5705427359, "2022-10-06"],
+  [5735455201, "2022-10-07"],
+  [5744374534, "2022-10-10"],
+  [5681900282, "2022-10-17"],
+  [5595045952, "2022-10-23"],
+  [5340744210, "2022-10-26"],
+  [5765259845, "2022-10-30"],
+  [5558980075, "2022-10-30"],
+  [5472518401, "2022-11-02"],
+  [5795660441, "2022-11-06"],
+  [5559594088, "2022-11-10"],
+  [5931294587, "2022-11-19"],
+  [5859861622, "2022-11-19"],
+  [5627539474, "2022-12-02"],
+  [5862080962, "2022-12-13"],
+  [5839137822, "2022-12-16"],
+  [5983753471, "2022-12-23"],
+  [5815100469, "2022-12-29"],
+  [5964221956, "2023-01-09"],
+  [5806925457, "2023-01-12"],
+  [5567880858, "2023-01-25"],
+  [6271031786, "2023-02-12"],
+  [6254094947, "2023-02-27"],
+  [5804028268, "2023-03-05"],
+  [6277658932, "2023-03-17"],
+  [5802659303, "2023-03-21"],
+  [5869978651, "2023-03-24"],
+  [5738347976, "2023-04-25"],
+  [5854845236, "2023-04-27"],
+  [6074830852, "2023-05-01"],
+  [5891297818, "2023-05-04"],
+  [6175817126, "2023-05-07"],
+  [6001287799, "2023-05-10"],
+  [5994561143, "2023-05-16"],
+  [6108395402, "2023-05-18"],
+  [5904140174, "2023-05-20"],
+  [6135597783, "2023-05-24"],
+  [6180394472, "2023-05-29"],
+  [6000582627, "2023-05-30"],
+  [6188508923, "2023-06-22"],
+  [6326011828, "2023-07-07"],
+  [6523424924, "2023-08-02"],
+  [6684986493, "2023-09-25"],
+  [6765129195, "2023-11-02"],
+  [6827058708, "2023-11-06"],
+  [6514802524, "2023-11-16"],
+  [6749492866, "2023-11-22"],
+  [6401027363, "2023-11-25"],
+  [6451891234, "2023-12-02"],
+  [6513268158, "2023-12-02"],
+  [6829119388, "2023-12-02"],
+  [6947316117, "2023-12-15"],
+  [6545049031, "2023-12-19"],
+  [6926984452, "2024-01-09"],
+  [6536173556, "2024-01-11"],
+  [6670760749, "2024-01-14"],
+  [6903333095, "2024-01-31"],
+  [6854829938, "2024-02-01"],
+  [6715889959, "2024-02-05"],
+  [6732829831, "2024-02-11"],
+  [6559717847, "2024-02-25"],
+  [6720229740, "2024-03-17"],
+  [6606876583, "2024-03-17"],
+  [7002435197, "2024-04-06"],
+  [7104310277, "2024-04-19"],
+  [6703731755, "2024-05-05"],
+  [7085776398, "2024-05-10"],
+  [6651640269, "2024-05-18"],
+  [6872061796, "2024-05-25"],
+  [7242296450, "2024-05-29"],
+  [7254607307, "2024-06-10"],
+  [7293965553, "2024-06-16"],
+  [7409259451, "2024-06-20"],
+  [7280136256, "2024-07-04"],
+  [7363299295, "2024-07-25"],
+  [7224009547, "2024-08-02"],
+  [7458668365, "2024-08-02"],
+  [7243375923, "2024-08-25"],
+  [7078066115, "2024-09-08"],
+  [7357703634, "2024-09-10"],
+  [7832006200, "2024-09-19"],
+  [7793034911, "2024-09-23"],
+  [7747102337, "2024-11-06"],
+  [7831448272, "2024-11-11"],
+  [7273085448, "2024-11-21"],
+  [7450316621, "2024-12-02"],
+  [7664959631, "2024-12-19"],
+  [7342300216, "2025-01-16"],
+  [7825518194, "2025-01-16"],
+  [8173852075, "2025-02-21"],
+  [8135088730, "2025-03-05"],
+  [8044853035, "2025-03-20"],
+  [7591351660, "2025-03-21"],
+  [7964511972, "2025-03-26"],
+  [7899152800, "2025-04-08"],
+  [7708562823, "2025-05-10"],
+  [7829910989, "2025-05-11"],
+  [7852083588, "2025-06-02"],
+  [7870888707, "2025-06-08"],
+  [7817256746, "2025-06-18"],
+  [7915901421, "2025-07-07"],
+  [8179125032, "2025-07-09"],
+  [8238766847, "2025-07-31"],
+  [8343786378, "2025-08-08"],
+  [8369442459, "2025-08-08"],
+  [8096742229, "2025-08-31"],
+  [7834356221, "2025-09-01"],
+  [8461579295, "2025-09-11"],
+  [8200159552, "2025-09-12"],
+  [7912577935, "2025-09-15"],
+  [8017192943, "2025-10-05"],
+  [8325327280, "2025-10-13"],
+  [8117852491, "2025-10-14"],
+  [8384648263, "2025-10-23"],
+  [8393200797, "2025-10-25"],
+  [8209194945, "2025-10-26"],
+  [8480708838, "2025-11-05"],
+  [8559682245, "2025-11-11"]
+]
diff --git a/examples/php/estimate.php b/examples/php/estimate.php
new file mode 100644
index 0000000..686f624
--- /dev/null
+++ b/examples/php/estimate.php
@@ -0,0 +1,43 @@
+ $a[0] <=> $b[0]);
+
+    if ($userId <= $points[0][0]) {
+        return new DateTimeImmutable($points[0][1]);
+    }
+
+    foreach ($points as $i => $p) {
+        if (!isset($points[$i+1])) break;
+
+        [$id1, $d1] = $points[$i];
+        [$id2, $d2] = $points[$i+1];
+
+        if ($userId >= $id1 && $userId <= $id2) {
+            return interpolate($userId, $id1, $d1, $id2, $d2);
+        }
+    }
+
+    [$id1, $d1] = $points[count($points)-2];
+    [$id2, $d2] = $points[count($points)-1];
+
+    return interpolate($userId, $id1, $d1, $id2, $d2);
+}
+
+function interpolate(int $uid, int $id1, string $d1, int $id2, string $d2): DateTimeImmutable
+{
+    if ($id1 === $id2) {
+        return new DateTimeImmutable($d1);
+    }
+
+    $t1 = strtotime($d1);
+    $t2 = strtotime($d2);
+    $ratio = ($uid - $id1) / ($id2 - $id1);
+
+    $ts = (int) ($t1 + ($t2 - $t1) * $ratio);
+    $res = (new DateTimeImmutable())->setTimestamp($ts);
+
+    return $res > new DateTimeImmutable() ? new DateTimeImmutable() : $res;
+}
diff --git a/examples/python/estimate.py b/examples/python/estimate.py
new file mode 100644
index 0000000..4c0d755
--- /dev/null
+++ b/examples/python/estimate.py
@@ -0,0 +1,29 @@
+from datetime import datetime
+
+def interpolate(uid, id1, d1, id2, d2):
+    if id1 == id2:
+        return datetime.fromisoformat(d1)
+
+    t1 = datetime.fromisoformat(d1).timestamp()
+    t2 = datetime.fromisoformat(d2).timestamp()
+
+    ratio = (uid - id1) / (id2 - id1)
+    ts = t1 + (t2 - t1) * ratio
+
+    result = datetime.fromtimestamp(ts)
+    return min(result, datetime.utcnow())
+
+def estimate_telegram_creation_date(user_id, points):
+    points.sort(key=lambda x: x[0])
+
+    if user_id <= points[0][0]:
+        return datetime.fromisoformat(points[0][1])
+
+    for i in range(len(points)-1):
+        id1, d1 = points[i]
+        id2, d2 = points[i+1]
+
+        if id1 <= user_id <= id2:
+            return interpolate(user_id, id1, d1, id2, d2)
+
+    return interpolate(user_id, *points[-2], *points[-1])