Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

16 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

                          TG.JoinProof
    _            __     __  _ _             __          __  _
   | |           \ \   / / | | |            \ \        / / | |
   | |__  _   _   \ \_/ /__| | | _____      _\ \  /\  / /__| |__
   | '_ \| | | |   \   / _ \ | |/ _ \ \ /\ / /\ \/  \/ / _ \ '_ \
   | |_) | |_| |    | |  __/ | | (_) \ V  V /  \  /\  /  __/ |_) |
   |_.__/ \__, |    |_|\___|_|_|\___/ \_/\_/    \/  \/ \___|_.__/
           __/ |
          |___/             https://yellowweb.top

If you like this script, PLEASE DONATE!

Поддержать проект

TG.JoinProof

English version

TG.JoinProof связывает рекламный click_id с фактическим вступлением пользователя в Telegram-канал или чат. Система создаёт персональную инвайт-ссылку, принимает событие chat_member от Telegram и отправляет GET-постбэк в YellowTDS, AIO, Binom или другой трекер.

Проект работает на PHP и SQLite без Composer и внешних PHP-библиотек. Интерфейс администратора доступен на русском и английском языках.

Возможности

  • Telegram-каналы, обычные группы и супергруппы.
  • Несколько ботов и несколько целей у одного бота.
  • Персональный инвайт на одного участника со сроком действия 24 часа.
  • Одна запись и одна конверсия для пары target + click_id.
  • Повторное использование действующего инвайта и автоматическое обновление истёкшего.
  • Отзыв персонального инвайта сразу после вступления.
  • Fallback после состоявшегося вступления: публичная ссылка Telegram либо общий инвайт приватной цели.
  • До трёх попыток доставки постбэка.
  • Макросы click ID и данных пользователя Telegram.
  • Статистика, журналы событий и RU/EN-интерфейс.

Скриншоты

Панель с подключённым приватным чатом:

Панель TG.JoinProof на русском

Английская версия того же экрана:

TG.JoinProof administration panel in English

Форма добавления канала или чата:

Форма добавления цели

Как это работает

  1. Трекер подставляет свой click ID в URL прямого редиректа:

    https://example.com/tg-postback/subscribe.php?target=-1003410571937&click_id={clickid}
    
  2. В target передаётся числовой Telegram ID канала или чата, например -1003410571937. @username в публичной ссылке не используется.

  3. При первом запросе система создаёт через Telegram Bot API инвайт с member_limit=1 и сроком действия 24 часа, сохраняет его вместе с click_id и перенаправляет пользователя в Telegram.

  4. Telegram присылает chat_member, когда пользователь действительно вступает. Система сопоставляет бота, Telegram chat ID и использованный инвайт.

  5. Инвайт отмечается использованным и отзывается, после чего в трекер отправляется postback.

Вступлением считается новый статус участника member, а также restricted с is_member=true. Изменение ограничений уже состоящего в чате пользователя не создаёт новую конверсию.

Жизненный цикл ссылки

Состояние пары target + click_id Результат запроса
Записи ещё нет Создаётся персональный инвайт на одного участника сроком на 24 часа
Инвайт действует, вступления не было Возвращается тот же персональный инвайт
Инвайт истёк, вступления не было Создаётся новый текущий инвайт; старый остаётся в истории для задержанного Telegram update
Вступление уже зафиксировано Новая конверсия не создаётся; пользователь направляется по fallback-ссылке

Для публичной цели fallback — https://t.me/username. Для приватной цели при её добавлении автоматически создаётся отдельный общий инвайт без отслеживания. Один и тот же click_id можно использовать для разных target: это независимые пары.

Конверсия фиксируется только при реальном переходе из состояния «не участник» в состояние «участник». Если человек уже состоит в канале или чате, нового события вступления и новой конверсии не будет.

Требования

  • PHP 8.1 или новее.
  • Расширения PHP: curl, json, pdo_sqlite.
  • Nginx, Apache или другой веб-сервер с PHP-FPM.
  • Публично доступный HTTPS-адрес с действительным сертификатом для Telegram webhook.
  • Telegram-бот с правами администратора в каждой подключённой цели.

Установка

1. Получение проекта

git clone https://github.com/dvygolov/TG.JoinProof.git
cd TG.JoinProof
cp .env.example .env

Проект использует чистую схему SQLite и не содержит миграций. Схема создаётся автоматически при первом обращении к панели, если по указанному DB_PATH нет таблиц. Непустая база не изменяется.

2. Настройка .env

Минимальная конфигурация:

DB_PATH=database.sqlite
APP_URL=https://example.com
LOG_PATH=logs/

INVITE_TTL=86400
POSTBACK_MAX_ATTEMPTS=3
POSTBACK_TIMEOUT=5

ADMIN_PASSWORD=replace_with_a_long_random_password

Основные параметры:

Переменная Назначение
DB_PATH Относительный от каталога проекта или абсолютный путь к SQLite
APP_URL Внешний адрес приложения без завершающего /
LOG_PATH Относительный или абсолютный каталог журналов
INVITE_TTL Срок персонального инвайта в секундах; штатное значение 86400
POSTBACK_MAX_ATTEMPTS Максимальное число попыток; штатное значение 3
POSTBACK_TIMEOUT Таймаут одной попытки в секундах; штатное значение 5
ADMIN_PASSWORD Пароль входа в панель

Для установки в подпапку укажите полный префикс:

APP_URL=https://example.com/tg-postback

Именно APP_URL используется при регистрации webhook и формировании публичной ссылки.

3. Создание базы

Для production рекомендуется хранить SQLite в отдельном непубличном каталоге. Укажите в .env:

DB_PATH=/var/lib/tg-joinproof/database.sqlite

Создайте каталог базы и каталог журналов до запуска установки:

sudo install -d -o www-data -g www-data -m 770 /var/lib/tg-joinproof
sudo chown -R www-data:www-data logs
sudo chmod 770 logs

SQLite создаёт journal/WAL-файлы рядом с базой, поэтому PHP-процессу веб-сервера нужны права записи не только на файл, но и на родительский каталог. Саму .env оставьте доступной только владельцу и группе веб-сервера:

sudo chown root:www-data .env
sudo chmod 640 .env

Не размещайте .env, SQLite и logs/ внутри публичного каталога. Веб-корнем должен быть только каталог public/.

4. Настройка веб-сервера в корне домена

Минимальный пример Nginx:

server {
    listen 443 ssl;
    server_name example.com;

    root /var/www/tg-postback/public;
    index index.php;

    location / {
        try_files $uri $uri/ =404;
    }

    location ~ \.php$ {
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_pass unix:/run/php/php8.3-fpm.sock;
    }
}

Путь к сокету PHP-FPM зависит от установленной версии PHP. SSL-параметры и сертификат добавьте в соответствии с конфигурацией сервера.

Для Apache задайте DocumentRoot на /var/www/tg-postback/public и разрешите обработку PHP в этом каталоге. В проекте уже есть public/.htaccess.

5. Настройка в подпапке

Пример для адреса https://example.com/tg-postback/ в существующем Nginx-сервере:

location = /tg-postback {
    return 301 /tg-postback/;
}

# Разместите этот блок раньше общего обработчика PHP сайта.
location ~ ^/tg-postback/(.+\.php)$ {
    include fastcgi_params;
    fastcgi_param SCRIPT_FILENAME /var/www/tg-postback/public/$1;
    fastcgi_pass unix:/run/php/php8.3-fpm.sock;
}

location /tg-postback/ {
    alias /var/www/tg-postback/public/;
    index index.php;
}

После изменения Nginx:

sudo nginx -t
sudo systemctl reload nginx

Для Apache можно использовать:

Alias /tg-postback/ /var/www/tg-postback/public/

<Directory /var/www/tg-postback/public>
    DirectoryIndex index.php
    AllowOverride All
    Require all granted
</Directory>

6. Локальный запуск

Для проверки интерфейса без Telegram webhook:

php -S 127.0.0.1:8080 -t public

Панель откроется по адресу http://127.0.0.1:8080. Полный сценарий вступления требует публичного HTTPS URL, доступного серверам Telegram.

Настройка Telegram

Создание и права бота

  1. Создайте бота через @BotFather и сохраните токен.
  2. Добавьте бота администратором нужного канала или чата.
  3. Разрешите боту приглашать пользователей — в Telegram это право называется can_invite_users, «Приглашение пользователей» или “Invite users via link”.
  4. Остальные административные права для работы системы не требуются.

Один Telegram-бот может иметь только один webhook. Не используйте подключённого бота одновременно в другом приложении, которое также вызывает setWebhook.

Добавление бота и цели

  1. Откройте панель по адресу из APP_URL и войдите с паролем из .env.
  2. В разделе «Боты» добавьте название и токен. Система проверит токен.
  3. В разделе «Чаты и каналы» добавьте цель:
    • выберите бота;
    • укажите числовой Telegram ID канала или чата (например -1003410571937) либо @username публичной цели; ссылку вида t.me/name система тоже примет;
    • при необходимости задайте отображаемое название;
    • вставьте URL постбэка трекера.
  4. При добавлении система проверит тип цели и права бота, сохранит канонический Telegram chat ID, подготовит fallback и зарегистрирует webhook.
  5. Нажмите кнопку копирования у созданной цели. Полученная ссылка содержит Telegram ID цели в target и учитывает подпапку из APP_URL.

В интерфейсе обычная группа и супергруппа отображаются единым типом «Чат», канал — типом «Канал».

Публичный redirect и YellowTDS

Единственный поддерживаемый публичный формат:

GET /subscribe.php?target={telegram_chat_id}&click_id={click_id}

Пример для Telegram-чата с ID -1003410571937:

https://example.com/tg-postback/subscribe.php?target=-1003410571937&click_id=abc123

Параметры:

Параметр Описание
target Числовой Telegram ID канала или чата, включая начальный -
click_id Click ID трекера длиной от 1 до 255 символов; передавайте его с URL-кодированием

YellowTDS

В настройках потока YellowTDS укажите прямой URL редиректа:

https://example.com/tg-postback/subscribe.php?target=-1003410571937&click_id={clickid}

YellowTDS подставит значение {clickid} и отправит клик напрямую в subscribe.php. Для другого трекера замените {clickid} на его макрос click ID. Значение target — постоянный Telegram ID подключённого канала или чата; готовую ссылку можно скопировать из панели.

При успешном запросе endpoint отвечает HTTP-редиректом на Telegram. Ошибка параметров возвращает 400, неизвестная или выключенная цель — 404, ошибка Telegram API при создании ссылки — 502, прочая ошибка приложения — 500.

Postback URL и макросы

Postback отправляется GET-запросом после подтверждённого вступления. Минимальный универсальный шаблон:

https://tracker.example/postback?click_id={clickid}&status={status}

Доступные макросы:

Макрос Значение
{clickid} Значение входного параметра click_id
{status} Статус конверсии; при вступлении — lead
{user_id} Числовой Telegram ID пользователя
{first_name} Имя
{last_name} Фамилия, если указана
{username} Telegram username без @, если указан
{is_premium} true или false
{language_code} Код языка Telegram, например ru или en

Значения автоматически URL-кодируются. Если Telegram не передал необязательное поле, соответствующий макрос заменяется пустой строкой.

Пример расширенного шаблона:

https://tracker.example/postback?click_id={clickid}&status={status}&telegram_id={user_id}&name={first_name}&username={username}&premium={is_premium}&lang={language_code}

При неуспешной доставке система делает не более трёх попыток: сразу, затем после пауз 1 и 2 секунды. Таймаут каждой попытки — 5 секунд. Результат и число попыток сохраняются в SQLite. После успешной доставки повторно полученный Telegram update не отправляет тот же postback ещё раз.

Проверка установки

Проверка синтаксиса и автоматических сценариев:

find app public tests -name '*.php' -print0 | xargs -0 -n1 php -l
php tests/run.php

Проверка публичного endpoint без перехода в Telegram:

curl -sS -D - -o /dev/null \
  "https://example.com/tg-postback/subscribe.php?target=-1003410571937&click_id=readme-test-001"

Ожидается HTTP-редирект с заголовком Location: https://t.me/....

Проверка webhook зарегистрированного бота:

curl -sS "https://api.telegram.org/bot<BOT_TOKEN>/getWebhookInfo"

В ответе проверьте:

  • url совпадает с APP_URL и заканчивается на webhook.php?...;
  • pending_update_count не растёт постоянно;
  • last_error_message отсутствует.

Не публикуйте вывод команды с токеном или закрытыми URL.

Контрольный E2E-сценарий

  1. Откройте ссылку с новым тестовым click_id.
  2. Убедитесь, что браузер перенаправлен на персональный инвайт Telegram.
  3. Вступите тестовым пользователем, который ещё не состоит в цели.
  4. Проверьте новую конверсию в трекере.
  5. Проверьте счётчик подписок в панели и postback.log.
  6. Повторите исходную ссылку: новой конверсии быть не должно, переход должен идти по fallback.

Журналы и диагностика

Журналы находятся в LOG_PATH:

Файл Содержимое
app.log Создание и изменение сущностей, инвайты, служебные события
webhook.log Полученные события вступления
postback.log Попытки и успешная доставка postback
errors.log Ошибки Telegram API, postback, базы и авторизации

Просмотр в реальном времени:

tail -f logs/app.log logs/webhook.log logs/postback.log logs/errors.log

Общая диагностика PHP, расширений, конфигурации, SQLite и прав записи:

php test.php

Для необязательной проверки Telegram API передайте токен только через окружение:

TELEGRAM_TEST_TOKEN='123456:secret' php test.php

Частые проблемы

В DB_PATH указана база от другой установки. Схема создаётся автоматически только в пустой базе. Если по указанному пути уже есть непустой файл SQLite без таблиц проекта, сохраните его отдельно и укажите новый путь. Автоматических миграций нет.

Цель не добавляется. Проверьте токен, членство бота, статус администратора, право приглашать пользователей и корректность @username либо числового chat ID.

Ссылка возвращает 400. Используйте только target и click_id. Убедитесь, что target — числовой Telegram ID канала или чата с начальным -, а click ID URL-кодирован.

Ссылка или панель возвращает 404 в подпапке. Проверьте одинаковый префикс в APP_URL, Nginx/Apache location и фактический путь к public/.

Telegram открывается, но конверсии нет. Пользователь должен вступить впервые именно по персональному инвайту. Проверьте getWebhookInfo, webhook.log, активность бота и цели.

Webhook получает ошибку. Адрес должен быть доступен Telegram по HTTPS без HTTP-авторизации и межсетевых ограничений. Сверьте URL из getWebhookInfo с APP_URL.

APP_URL был изменён после настройки. Сохраните каждого бота в панели ещё раз, чтобы зарегистрировать webhook с новым адресом.

Postback не приходит. Проверьте итоговый URL и макрос {clickid}, доступность трекера с сервера и postback.log/errors.log. После трёх неудачных попыток автоматическая доставка прекращается.

Повторный клик после вступления не создаёт конверсию. Это штатное поведение: для пары target + click_id конверсия создаётся один раз, а дальнейшие клики используют fallback.

Резервное копирование и обновление

Для резервной копии достаточно сохранить:

  • .env;
  • файл из DB_PATH;
  • конфигурацию веб-сервера.

Перед копированием активной SQLite остановите запись в приложение либо используйте штатную команду .backup утилиты sqlite3.

Текущая версия рассчитана на чистую схему и не выполняет миграции существующей базы. Перед установкой версии с другой схемой сохраните старую базу отдельно и укажите в DB_PATH путь к новой пустой базе: схема создастся автоматически при первом запросе.

Структура проекта

app/                 PHP-классы, модели и сервисы
public/              единственный document root
public/index.php     панель администратора
public/subscribe.php публичный redirect endpoint
public/webhook.php   Telegram webhook
tests/               автоматические проверки
logs/                журналы по умолчанию
database.sqlite      SQLite по умолчанию

Лицензия

MIT

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages