Add router icon / multi-format media support

This commit is contained in:
Capybara-z
2025-10-18 00:21:14 +03:00
parent 7090daefc5
commit 8fdd2dd3c4
3 changed files with 112 additions and 39 deletions
+14 -3
View File
@@ -96,7 +96,7 @@ async def process_callback_or_message_view_keys(callback_query_or_message: Messa
await render_key_info(target_message, session, key_name, image_path)
return
inline_keyboard, response_message = build_keys_response(records)
inline_keyboard, response_message = await build_keys_response(records, session)
image_path = os.path.join("img", "pic_keys.jpg")
await edit_or_send_message(
@@ -110,7 +110,7 @@ async def process_callback_or_message_view_keys(callback_query_or_message: Messa
await target_message.answer(text=error_message)
def build_keys_response(records):
async def build_keys_response(records, session):
"""
Формирует сообщение и клавиатуру для устройств с указанием срока действия подписки.
"""
@@ -133,7 +133,18 @@ def build_keys_response(records):
else:
formatted_date_full = "без срока действия"
key_button = InlineKeyboardButton(text=f"🔑 {key_display}", callback_data=f"view_key|{email}")
is_vless = False
if hasattr(record, 'tariff_id') and record.tariff_id:
try:
tariff = await get_tariff_by_id(session, record.tariff_id)
if tariff and tariff.get("vless"):
is_vless = True
except:
pass
icon = "📶" if is_vless else "🔑"
key_button = InlineKeyboardButton(text=f"{icon} {key_display}", callback_data=f"view_key|{email}")
rename_button = InlineKeyboardButton(text=ALIAS, callback_data=f"rename_key|{client_id}")
builder.row(key_button, rename_button)
+1
View File
@@ -203,6 +203,7 @@ def prepare_headers(
"announce": "base64:" + base64.b64encode(announce_str.encode("utf-8")).decode("utf-8"),
"profile-web-page-url": f"https://t.me/{USERNAME_BOT}",
"subscription-userinfo": subscription_userinfo,
#"routing": "happ://routing/onadd/...",
}
elif "Hiddify" in user_agent:
parts = subscription_info.split(" - ")[0].split(": ")
+97 -36
View File
@@ -15,6 +15,8 @@ from aiogram.types import (
BufferedInputFile,
InlineKeyboardMarkup,
InputMediaPhoto,
InputMediaVideo,
InputMediaAnimation,
Message,
)
from sqlalchemy import func, select
@@ -197,6 +199,24 @@ def format_hours(hours: int) -> str:
return f"{hours} {get_plural_form(hours, 'час', 'часа', 'часов')}"
def get_media_type(media_path: str) -> str:
if not media_path:
return 'photo'
ext = os.path.splitext(media_path.lower())[1]
if ext in ['.jpg', '.jpeg', '.png', '.webp']:
return 'photo'
if ext in ['.mp4', '.mov', '.avi']:
return 'video'
if ext == '.gif':
return 'animation'
return 'photo'
async def edit_or_send_message(
target_message: Message,
text: str,
@@ -212,47 +232,88 @@ async def edit_or_send_message(
edit_or_send_message.lock = asyncio.Lock()
edit_or_send_message.max = 256
if media_path and os.path.isfile(media_path):
async with edit_or_send_message.lock:
cached_id = edit_or_send_message.cache.get(media_path)
def find_media_file(original_path: str) -> str | None:
if not original_path:
return None
if os.path.isfile(original_path):
return original_path
base_name = os.path.splitext(original_path)[0]
supported_extensions = ['.jpg', '.jpeg', '.png', '.gif', '.webp', '.mp4', '.mov', '.avi']
for ext in supported_extensions:
fallback_path = base_name + ext
if os.path.isfile(fallback_path):
return fallback_path
return None
if media_path:
actual_media_path = find_media_file(media_path)
if actual_media_path:
media_type = get_media_type(actual_media_path)
async with edit_or_send_message.lock:
cached_id = edit_or_send_message.cache.get(actual_media_path)
if cached_id:
edit_or_send_message.cache.move_to_end(actual_media_path)
if cached_id:
edit_or_send_message.cache.move_to_end(media_path)
if cached_id:
try:
await target_message.edit_media(InputMediaPhoto(media=cached_id, caption=text), reply_markup=reply_markup)
return
except Exception:
try:
await target_message.answer_photo(
photo=cached_id,
caption=text,
reply_markup=reply_markup,
disable_web_page_preview=disable_web_page_preview,
)
if media_type == 'photo':
await target_message.edit_media(InputMediaPhoto(media=cached_id, caption=text), reply_markup=reply_markup)
elif media_type == 'video':
await target_message.edit_media(InputMediaVideo(media=cached_id, caption=text), reply_markup=reply_markup)
elif media_type == 'animation':
await target_message.edit_media(InputMediaAnimation(media=cached_id, caption=text), reply_markup=reply_markup)
return
except Exception:
pass
try:
if media_type == 'photo':
await target_message.answer_photo(photo=cached_id, caption=text, reply_markup=reply_markup, disable_web_page_preview=disable_web_page_preview)
elif media_type == 'video':
await target_message.answer_video(video=cached_id, caption=text, reply_markup=reply_markup, disable_web_page_preview=disable_web_page_preview)
elif media_type == 'animation':
await target_message.answer_animation(animation=cached_id, caption=text, reply_markup=reply_markup, disable_web_page_preview=disable_web_page_preview)
return
except Exception:
pass
async with aiofiles.open(media_path, "rb") as f:
data = await f.read()
upload = BufferedInputFile(data, filename=os.path.basename(media_path))
try:
msg = await target_message.edit_media(InputMediaPhoto(media=upload, caption=text), reply_markup=reply_markup)
except Exception:
msg = await target_message.answer_photo(
photo=upload,
caption=text,
reply_markup=reply_markup,
disable_web_page_preview=disable_web_page_preview,
)
if getattr(msg, "photo", None):
fid = msg.photo[-1].file_id
async with edit_or_send_message.lock:
if media_path not in edit_or_send_message.cache:
edit_or_send_message.cache[media_path] = fid
if len(edit_or_send_message.cache) > edit_or_send_message.max:
edit_or_send_message.cache.popitem(last=False)
return
async with aiofiles.open(actual_media_path, "rb") as f:
data = await f.read()
upload = BufferedInputFile(data, filename=os.path.basename(actual_media_path))
try:
if media_type == 'photo':
msg = await target_message.edit_media(InputMediaPhoto(media=upload, caption=text), reply_markup=reply_markup)
elif media_type == 'video':
msg = await target_message.edit_media(InputMediaVideo(media=upload, caption=text), reply_markup=reply_markup)
elif media_type == 'animation':
msg = await target_message.edit_media(InputMediaAnimation(media=upload, caption=text), reply_markup=reply_markup)
except Exception:
if media_type == 'photo':
msg = await target_message.answer_photo(photo=upload, caption=text, reply_markup=reply_markup, disable_web_page_preview=disable_web_page_preview)
elif media_type == 'video':
msg = await target_message.answer_video(video=upload, caption=text, reply_markup=reply_markup, disable_web_page_preview=disable_web_page_preview)
elif media_type == 'animation':
msg = await target_message.answer_animation(animation=upload, caption=text, reply_markup=reply_markup, disable_web_page_preview=disable_web_page_preview)
file_id = None
if hasattr(msg, 'photo') and msg.photo:
file_id = msg.photo[-1].file_id
elif hasattr(msg, 'video') and msg.video:
file_id = msg.video.file_id
elif hasattr(msg, 'animation') and msg.animation:
file_id = msg.animation.file_id
if file_id:
async with edit_or_send_message.lock:
if actual_media_path not in edit_or_send_message.cache:
edit_or_send_message.cache[actual_media_path] = file_id
if len(edit_or_send_message.cache) > edit_or_send_message.max:
edit_or_send_message.cache.popitem(last=False)
return
if not force_text and target_message.caption is not None:
try: