From 2f29be7ec78474ddb18063fe3cc061f1c7286dac Mon Sep 17 00:00:00 2001 From: crutoboy Date: Fri, 29 May 2026 09:11:51 +0000 Subject: [PATCH] add sub metadata support --- .dockerignore | 1 + .env.example | 17 +++++++++++++++++ .gitignore | 1 + config.py | 7 +++++++ docker-compose.yml | 5 +++++ main.py | 30 ++++++++++++++++++++++++++++-- 6 files changed, 59 insertions(+), 2 deletions(-) diff --git a/.dockerignore b/.dockerignore index e2795ae..764a037 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,4 +1,5 @@ certs +test # Byte-compiled / optimized / DLL files __pycache__/ diff --git a/.env.example b/.env.example index 797fca3..848147b 100644 --- a/.env.example +++ b/.env.example @@ -10,6 +10,23 @@ URI_PATH=/sub/ # Базовый путь URL для подписк # Время кеширования внешних подписок (в секундах). По умолчанию 1 час. SUBSCRIPTION_CACHE_TTL=3600 +# ГЛОБАЛЬНЫЕ МЕТАДАННЫЕ ПОДПИСКИ +# Ссылка на поддержку (отображается в клиентах) +SUPPORT_URL=https://t.me/your_support + +# Ссылка на профиль / панель управления +PROFILE_WEB_PAGE_URL=https://panel.example.com + +# Объявление / важное сообщение (показывается в некоторых клиентах) +ANNOUNCE=Это объявление + +# Как часто клиенты должны обновлять подписку (в часах) +UPDATE_INTERVAL=12 + +# Ссылка на импорт маршрутизации для клиента Happ (happ://routing/add/...) +# Скопируй полную ссылку из Happ или сгенерируй самостоятельно +HAPP_ROUTING_LINK=happ://routing/add/eyJibG9ja2lwIjpbXSwiYmxvY2tzaXRlcyI6W10sImRpcmVjdGlwIjpbIjEwLjAuMC4wLzgiLCIxNzIuMTYuMC4wLzEyIiwiMTkyLjE2OC4wLjAvMTYiLCIxNjkuMjU0LjAuMC8xNiIsIjIyNC4wLjAuMC80IiwiMjU1LjI1NS4yNTUuMjU1IiwiZ2VvaXA6cnUiXSwiZGlyZWN0c2l0ZXMiOlsiZ2Vvc2l0ZTpjYXRlZ29yeS1ydSIsIioubG9jYWwiXSwiZG5zaG9zdHMiOnsiY2xvdWRmbGFyZS1kbnMuY29tIjoiMS4xLjEuMSIsImRucy5nb29nbGUiOiI4LjguOC44In0sImRvbWFpbnN0cmF0ZWd5IjoiSVBJZk5vbk1hdGNoIiwiZG9tZXN0aWNkbnNkb21haW4iOiJodHRwczovL2Rucy5nb29nbGUvZG5zLXF1ZXJ5IiwiZG9tZXN0aWNkbnNpcCI6IjguOC44LjgiLCJkb21lc3RpY2Ruc3R5cGUiOiJEb0giLCJmYWtlZG5zIjpmYWxzZSwiZ2VvaXB1cmwiOiJodHRwczovL2dpdGh1Yi5jb20vTG95YWxzb2xkaWVyL3YycmF5LXJ1bGVzLWRhdGEvcmVsZWFzZXMvbGF0ZXN0L2Rvd25sb2FkL2dlb2lwLmRhdCIsImdlb3NpdGV1cmwiOiJodHRwczovL2dpdGh1Yi5jb20vTG95YWxzb2xkaWVyL3YycmF5LXJ1bGVzLWRhdGEvcmVsZWFzZXMvbGF0ZXN0L2Rvd25sb2FkL2dlb3NpdGUuZGF0IiwiZ2xvYmFscHJveHkiOnRydWUsIm5hbWUiOiJydSwgbG9jYWwiLCJwcm94eWlwIjpbXSwicHJveHlzaXRlcyI6W10sInJlbW90ZWRuc2RvbWFpbiI6Imh0dHBzOi8vY2xvdWRmbGFyZS1kbnMuY29tL2Rucy1xdWVyeSIsInJlbW90ZWRuc2lwIjoiMS4xLjEuMSIsInJlbW90ZWRuc3R5cGUiOiJEb0giLCJyb3V0ZW9yZGVyIjoiYmxvY2stZGlyZWN0LXByb3h5In0= + # ===================================================================== # URLS — основные ссылки и конфигурации, которые будут возвращаться пользователю diff --git a/.gitignore b/.gitignore index e2795ae..764a037 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ certs +test # Byte-compiled / optimized / DLL files __pycache__/ diff --git a/config.py b/config.py index 75df1ec..ebfaa11 100644 --- a/config.py +++ b/config.py @@ -14,3 +14,10 @@ URLS = json.loads(os.getenv('URLS', '{}')) # TTL кеша внешних подписок в секундах (по умолчанию 1 час) SUBSCRIPTION_CACHE_TTL = int(os.getenv('SUBSCRIPTION_CACHE_TTL', '3600')) + +# метаданные подписки +SUPPORT_URL = os.getenv('SUPPORT_URL', '') +PROFILE_WEB_PAGE_URL = os.getenv('PROFILE_WEB_PAGE_URL', '') +ANNOUNCE = os.getenv('ANNOUNCE', '') +UPDATE_INTERVAL = int(os.getenv('UPDATE_INTERVAL', '12')) # в часах +HAPP_ROUTING_LINK = os.getenv('HAPP_ROUTING_LINK', '') # полный happ://routing/add/... diff --git a/docker-compose.yml b/docker-compose.yml index 2ee5053..81a19bb 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -28,4 +28,9 @@ services: # LISTEN_PORT: 2096 # URI_PATH: /sub/ # SUBSCRIPTION_CACHE_TTL: 3600 + # SUPPORT_URL: https://t.me/your_support + # PROFILE_WEB_PAGE_URL: https://panel.example.com + # ANNOUNCE: Это объявление + # UPDATE_INTERVAL: 12 + # HAPP_ROUTING_LINK: happ://routing/add/... # URLS: '{"all": [...]}' # большой JSON лучше хранить в .env файле diff --git a/main.py b/main.py index 846929d..fde1906 100644 --- a/main.py +++ b/main.py @@ -2,6 +2,7 @@ from typing import List import base64 import flask +from flask import make_response import requests from cachetools import TTLCache, cached @@ -51,8 +52,33 @@ def format_urls(urls: List[str], user: str) -> List[str]: def get_subs(user: str): urls = format_urls(c.URLS.get(user, []) + c.URLS.get('all', []), user) urls_text = '\n'.join(urls) - res = base64.b64encode(bytes(urls_text, 'utf-8')) - return res + + encoded = base64.b64encode(bytes(urls_text, 'utf-8')) + + # Создаём ответ и добавляем заголовки + resp = make_response(encoded) + resp.headers['Content-Type'] = 'text/plain; charset=utf-8' + + if c.UPDATE_INTERVAL: + resp.headers['Profile-Update-Interval'] = str(c.UPDATE_INTERVAL) + + if c.SUPPORT_URL: + resp.headers['Support-Url'] = c.SUPPORT_URL + + if c.PROFILE_WEB_PAGE_URL: + resp.headers['Profile-Web-Page-Url'] = c.PROFILE_WEB_PAGE_URL + + if c.ANNOUNCE: + announce_bytes = base64.b64encode(bytes(c.ANNOUNCE, "utf-8")) + announce_encode = announce_bytes.decode('ascii') + resp.headers['Announce'] = f'base64:{announce_encode}' + + if c.HAPP_ROUTING_LINK: + resp.headers['Routing'] = c.HAPP_ROUTING_LINK + resp.headers['Routing-Enable'] = 'true' + + return resp + if __name__ == '__main__':