Add router icon / multi-format media support
This commit is contained in:
@@ -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)
|
await render_key_info(target_message, session, key_name, image_path)
|
||||||
return
|
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")
|
image_path = os.path.join("img", "pic_keys.jpg")
|
||||||
|
|
||||||
await edit_or_send_message(
|
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)
|
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:
|
else:
|
||||||
formatted_date_full = "без срока действия"
|
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}")
|
rename_button = InlineKeyboardButton(text=ALIAS, callback_data=f"rename_key|{client_id}")
|
||||||
builder.row(key_button, rename_button)
|
builder.row(key_button, rename_button)
|
||||||
|
|
||||||
|
|||||||
@@ -203,6 +203,7 @@ def prepare_headers(
|
|||||||
"announce": "base64:" + base64.b64encode(announce_str.encode("utf-8")).decode("utf-8"),
|
"announce": "base64:" + base64.b64encode(announce_str.encode("utf-8")).decode("utf-8"),
|
||||||
"profile-web-page-url": f"https://t.me/{USERNAME_BOT}",
|
"profile-web-page-url": f"https://t.me/{USERNAME_BOT}",
|
||||||
"subscription-userinfo": subscription_userinfo,
|
"subscription-userinfo": subscription_userinfo,
|
||||||
|
#"routing": "happ://routing/onadd/...",
|
||||||
}
|
}
|
||||||
elif "Hiddify" in user_agent:
|
elif "Hiddify" in user_agent:
|
||||||
parts = subscription_info.split(" - ")[0].split(": ")
|
parts = subscription_info.split(" - ")[0].split(": ")
|
||||||
|
|||||||
+97
-36
@@ -15,6 +15,8 @@ from aiogram.types import (
|
|||||||
BufferedInputFile,
|
BufferedInputFile,
|
||||||
InlineKeyboardMarkup,
|
InlineKeyboardMarkup,
|
||||||
InputMediaPhoto,
|
InputMediaPhoto,
|
||||||
|
InputMediaVideo,
|
||||||
|
InputMediaAnimation,
|
||||||
Message,
|
Message,
|
||||||
)
|
)
|
||||||
from sqlalchemy import func, select
|
from sqlalchemy import func, select
|
||||||
@@ -197,6 +199,24 @@ def format_hours(hours: int) -> str:
|
|||||||
return f"{hours} {get_plural_form(hours, 'час', 'часа', 'часов')}"
|
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(
|
async def edit_or_send_message(
|
||||||
target_message: Message,
|
target_message: Message,
|
||||||
text: str,
|
text: str,
|
||||||
@@ -212,47 +232,88 @@ async def edit_or_send_message(
|
|||||||
edit_or_send_message.lock = asyncio.Lock()
|
edit_or_send_message.lock = asyncio.Lock()
|
||||||
edit_or_send_message.max = 256
|
edit_or_send_message.max = 256
|
||||||
|
|
||||||
if media_path and os.path.isfile(media_path):
|
def find_media_file(original_path: str) -> str | None:
|
||||||
async with edit_or_send_message.lock:
|
if not original_path:
|
||||||
cached_id = edit_or_send_message.cache.get(media_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:
|
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:
|
try:
|
||||||
await target_message.answer_photo(
|
if media_type == 'photo':
|
||||||
photo=cached_id,
|
await target_message.edit_media(InputMediaPhoto(media=cached_id, caption=text), reply_markup=reply_markup)
|
||||||
caption=text,
|
elif media_type == 'video':
|
||||||
reply_markup=reply_markup,
|
await target_message.edit_media(InputMediaVideo(media=cached_id, caption=text), reply_markup=reply_markup)
|
||||||
disable_web_page_preview=disable_web_page_preview,
|
elif media_type == 'animation':
|
||||||
)
|
await target_message.edit_media(InputMediaAnimation(media=cached_id, caption=text), reply_markup=reply_markup)
|
||||||
return
|
return
|
||||||
except Exception:
|
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:
|
async with aiofiles.open(actual_media_path, "rb") as f:
|
||||||
data = await f.read()
|
data = await f.read()
|
||||||
upload = BufferedInputFile(data, filename=os.path.basename(media_path))
|
upload = BufferedInputFile(data, filename=os.path.basename(actual_media_path))
|
||||||
try:
|
|
||||||
msg = await target_message.edit_media(InputMediaPhoto(media=upload, caption=text), reply_markup=reply_markup)
|
try:
|
||||||
except Exception:
|
if media_type == 'photo':
|
||||||
msg = await target_message.answer_photo(
|
msg = await target_message.edit_media(InputMediaPhoto(media=upload, caption=text), reply_markup=reply_markup)
|
||||||
photo=upload,
|
elif media_type == 'video':
|
||||||
caption=text,
|
msg = await target_message.edit_media(InputMediaVideo(media=upload, caption=text), reply_markup=reply_markup)
|
||||||
reply_markup=reply_markup,
|
elif media_type == 'animation':
|
||||||
disable_web_page_preview=disable_web_page_preview,
|
msg = await target_message.edit_media(InputMediaAnimation(media=upload, caption=text), reply_markup=reply_markup)
|
||||||
)
|
except Exception:
|
||||||
if getattr(msg, "photo", None):
|
if media_type == 'photo':
|
||||||
fid = msg.photo[-1].file_id
|
msg = await target_message.answer_photo(photo=upload, caption=text, reply_markup=reply_markup, disable_web_page_preview=disable_web_page_preview)
|
||||||
async with edit_or_send_message.lock:
|
elif media_type == 'video':
|
||||||
if media_path not in edit_or_send_message.cache:
|
msg = await target_message.answer_video(video=upload, caption=text, reply_markup=reply_markup, disable_web_page_preview=disable_web_page_preview)
|
||||||
edit_or_send_message.cache[media_path] = fid
|
elif media_type == 'animation':
|
||||||
if len(edit_or_send_message.cache) > edit_or_send_message.max:
|
msg = await target_message.answer_animation(animation=upload, caption=text, reply_markup=reply_markup, disable_web_page_preview=disable_web_page_preview)
|
||||||
edit_or_send_message.cache.popitem(last=False)
|
|
||||||
return
|
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:
|
if not force_text and target_message.caption is not None:
|
||||||
try:
|
try:
|
||||||
|
|||||||
Reference in New Issue
Block a user