Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
df9e36928c |
+100
-75
@@ -11,16 +11,23 @@ from config.settings import Settings
|
|||||||
from db.dal import user_dal, message_log_dal
|
from db.dal import user_dal, message_log_dal
|
||||||
|
|
||||||
from bot.states.admin_states import AdminStates
|
from bot.states.admin_states import AdminStates
|
||||||
from bot.keyboards.inline.admin_keyboards import get_broadcast_confirmation_keyboard, get_back_to_admin_panel_keyboard, get_admin_panel_keyboard
|
from bot.keyboards.inline.admin_keyboards import (
|
||||||
|
get_broadcast_confirmation_keyboard,
|
||||||
|
get_back_to_admin_panel_keyboard,
|
||||||
|
get_admin_panel_keyboard,
|
||||||
|
)
|
||||||
from bot.middlewares.i18n import JsonI18n
|
from bot.middlewares.i18n import JsonI18n
|
||||||
|
|
||||||
router = Router(name="admin_broadcast_router")
|
router = Router(name="admin_broadcast_router")
|
||||||
|
|
||||||
|
|
||||||
async def broadcast_message_prompt_handler(callback: types.CallbackQuery,
|
async def broadcast_message_prompt_handler(
|
||||||
state: FSMContext, i18n_data: dict,
|
callback: types.CallbackQuery,
|
||||||
|
state: FSMContext,
|
||||||
|
i18n_data: dict,
|
||||||
settings: Settings,
|
settings: Settings,
|
||||||
session: AsyncSession):
|
session: AsyncSession,
|
||||||
|
):
|
||||||
current_lang = i18n_data.get("current_language", settings.DEFAULT_LANGUAGE)
|
current_lang = i18n_data.get("current_language", settings.DEFAULT_LANGUAGE)
|
||||||
i18n: Optional[JsonI18n] = i18n_data.get("i18n_instance")
|
i18n: Optional[JsonI18n] = i18n_data.get("i18n_instance")
|
||||||
if not i18n:
|
if not i18n:
|
||||||
@@ -35,25 +42,28 @@ async def broadcast_message_prompt_handler(callback: types.CallbackQuery,
|
|||||||
try:
|
try:
|
||||||
await callback.message.edit_text(
|
await callback.message.edit_text(
|
||||||
prompt_text,
|
prompt_text,
|
||||||
reply_markup=get_back_to_admin_panel_keyboard(
|
reply_markup=get_back_to_admin_panel_keyboard(current_lang, i18n),
|
||||||
current_lang, i18n))
|
)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.warning(
|
logging.warning(
|
||||||
f"Could not edit message for broadcast prompt: {e}. Sending new."
|
f"Could not edit message for broadcast prompt: {e}. Sending new."
|
||||||
)
|
)
|
||||||
await callback.message.answer(
|
await callback.message.answer(
|
||||||
prompt_text,
|
prompt_text,
|
||||||
reply_markup=get_back_to_admin_panel_keyboard(
|
reply_markup=get_back_to_admin_panel_keyboard(current_lang, i18n),
|
||||||
current_lang, i18n))
|
)
|
||||||
await callback.answer()
|
await callback.answer()
|
||||||
await state.set_state(AdminStates.waiting_for_broadcast_message)
|
await state.set_state(AdminStates.waiting_for_broadcast_message)
|
||||||
|
|
||||||
|
|
||||||
@router.message(AdminStates.waiting_for_broadcast_message, F.text)
|
@router.message(AdminStates.waiting_for_broadcast_message, F.text)
|
||||||
async def process_broadcast_message_handler(message: types.Message,
|
async def process_broadcast_message_handler(
|
||||||
state: FSMContext, i18n_data: dict,
|
message: types.Message,
|
||||||
|
state: FSMContext,
|
||||||
|
i18n_data: dict,
|
||||||
settings: Settings,
|
settings: Settings,
|
||||||
session: AsyncSession):
|
session: AsyncSession,
|
||||||
|
):
|
||||||
current_lang = i18n_data.get("current_language", settings.DEFAULT_LANGUAGE)
|
current_lang = i18n_data.get("current_language", settings.DEFAULT_LANGUAGE)
|
||||||
i18n: Optional[JsonI18n] = i18n_data.get("i18n_instance")
|
i18n: Optional[JsonI18n] = i18n_data.get("i18n_instance")
|
||||||
if not i18n:
|
if not i18n:
|
||||||
@@ -63,28 +73,35 @@ async def process_broadcast_message_handler(message: types.Message,
|
|||||||
|
|
||||||
_ = lambda key, **kwargs: i18n.gettext(current_lang, key, **kwargs)
|
_ = lambda key, **kwargs: i18n.gettext(current_lang, key, **kwargs)
|
||||||
|
|
||||||
broadcast_message_text = message.html_text
|
# Сохраняем в state исходный текст и entities
|
||||||
await state.update_data(broadcast_message=broadcast_message_text)
|
text = message.text or message.caption or ""
|
||||||
|
entities = message.entities or message.caption_entities or []
|
||||||
|
|
||||||
preview_snippet = (
|
await state.update_data(
|
||||||
broadcast_message_text[:200] +
|
broadcast_text=text,
|
||||||
"...") if len(broadcast_message_text) > 200 else broadcast_message_text
|
broadcast_entities=entities,
|
||||||
confirmation_prompt = _("admin_broadcast_confirm_prompt",
|
)
|
||||||
message_preview=preview_snippet)
|
|
||||||
|
|
||||||
await message.answer(confirmation_prompt,
|
preview_snippet = (text[:200] + "...") if len(text) > 200 else text
|
||||||
reply_markup=get_broadcast_confirmation_keyboard(
|
confirmation_prompt = _("admin_broadcast_confirm_prompt", message_preview=preview_snippet)
|
||||||
current_lang, i18n),
|
|
||||||
parse_mode="HTML")
|
await message.answer(
|
||||||
|
confirmation_prompt,
|
||||||
|
reply_markup=get_broadcast_confirmation_keyboard(current_lang, i18n),
|
||||||
|
)
|
||||||
await state.set_state(AdminStates.confirming_broadcast)
|
await state.set_state(AdminStates.confirming_broadcast)
|
||||||
|
|
||||||
|
|
||||||
@router.callback_query(F.data == "admin_action:main",
|
@router.callback_query(
|
||||||
AdminStates.waiting_for_broadcast_message)
|
F.data == "admin_action:main", AdminStates.waiting_for_broadcast_message
|
||||||
async def cancel_broadcast_at_prompt_stage(callback: types.CallbackQuery,
|
)
|
||||||
|
async def cancel_broadcast_at_prompt_stage(
|
||||||
|
callback: types.CallbackQuery,
|
||||||
state: FSMContext,
|
state: FSMContext,
|
||||||
settings: Settings, i18n_data: dict,
|
settings: Settings,
|
||||||
session: AsyncSession):
|
i18n_data: dict,
|
||||||
|
session: AsyncSession,
|
||||||
|
):
|
||||||
current_lang = i18n_data.get("current_language", settings.DEFAULT_LANGUAGE)
|
current_lang = i18n_data.get("current_language", settings.DEFAULT_LANGUAGE)
|
||||||
i18n: Optional[JsonI18n] = i18n_data.get("i18n_instance")
|
i18n: Optional[JsonI18n] = i18n_data.get("i18n_instance")
|
||||||
if not i18n or not callback.message:
|
if not i18n or not callback.message:
|
||||||
@@ -94,96 +111,104 @@ async def cancel_broadcast_at_prompt_stage(callback: types.CallbackQuery,
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
await callback.message.edit_text(
|
await callback.message.edit_text(
|
||||||
_("admin_broadcast_cancelled_nav_back"), reply_markup=None)
|
_("admin_broadcast_cancelled_nav_back"), reply_markup=None
|
||||||
|
)
|
||||||
except Exception:
|
except Exception:
|
||||||
await callback.message.answer(_("admin_broadcast_cancelled_nav_back"))
|
await callback.message.answer(_("admin_broadcast_cancelled_nav_back"))
|
||||||
|
|
||||||
await callback.answer(_("admin_broadcast_cancelled_alert"))
|
await callback.answer(_("admin_broadcast_cancelled_alert"))
|
||||||
await state.clear()
|
await state.clear()
|
||||||
|
|
||||||
await callback.message.answer(_(key="admin_panel_title"),
|
await callback.message.answer(
|
||||||
reply_markup=get_admin_panel_keyboard(
|
_(key="admin_panel_title"),
|
||||||
i18n, current_lang, settings))
|
reply_markup=get_admin_panel_keyboard(i18n, current_lang, settings),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@router.callback_query(F.data.startswith("broadcast_final_action:"),
|
@router.callback_query(
|
||||||
AdminStates.confirming_broadcast)
|
F.data.startswith("broadcast_final_action:"),
|
||||||
async def confirm_broadcast_callback_handler(callback: types.CallbackQuery,
|
AdminStates.confirming_broadcast,
|
||||||
|
)
|
||||||
|
async def confirm_broadcast_callback_handler(
|
||||||
|
callback: types.CallbackQuery,
|
||||||
state: FSMContext,
|
state: FSMContext,
|
||||||
i18n_data: dict, bot: Bot,
|
i18n_data: dict,
|
||||||
|
bot: Bot,
|
||||||
settings: Settings,
|
settings: Settings,
|
||||||
session: AsyncSession):
|
session: AsyncSession,
|
||||||
|
):
|
||||||
current_lang = i18n_data.get("current_language", settings.DEFAULT_LANGUAGE)
|
current_lang = i18n_data.get("current_language", settings.DEFAULT_LANGUAGE)
|
||||||
i18n: Optional[JsonI18n] = i18n_data.get("i18n_instance")
|
i18n: Optional[JsonI18n] = i18n_data.get("i18n_instance")
|
||||||
if not i18n or not callback.message:
|
if not i18n or not callback.message:
|
||||||
await callback.answer("Error processing broadcast confirmation.",
|
await callback.answer("Error processing broadcast confirmation.", show_alert=True)
|
||||||
show_alert=True)
|
|
||||||
return
|
return
|
||||||
_ = lambda key, **kwargs: i18n.gettext(current_lang, key, **kwargs)
|
_ = lambda key, **kwargs: i18n.gettext(current_lang, key, **kwargs)
|
||||||
|
|
||||||
action = callback.data.split(":")[1]
|
action = callback.data.split(":")[1]
|
||||||
user_fsm_data = await state.get_data()
|
user_fsm_data = await state.get_data()
|
||||||
broadcast_message_to_send = user_fsm_data.get("broadcast_message")
|
|
||||||
|
|
||||||
if action == "send":
|
if action == "send":
|
||||||
if not broadcast_message_to_send:
|
text = user_fsm_data.get("broadcast_text")
|
||||||
await callback.message.edit_text(
|
entities = user_fsm_data.get("broadcast_entities", [])
|
||||||
_("admin_broadcast_error_no_message"))
|
|
||||||
|
if not text:
|
||||||
|
await callback.message.edit_text(_("admin_broadcast_error_no_message"))
|
||||||
await state.clear()
|
await state.clear()
|
||||||
await callback.answer(_("admin_broadcast_error_no_message_alert"),
|
await callback.answer(
|
||||||
show_alert=True)
|
_("admin_broadcast_error_no_message_alert"), show_alert=True
|
||||||
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
await callback.message.edit_text(_("admin_broadcast_sending_started"),
|
await callback.message.edit_text(_("admin_broadcast_sending_started"), reply_markup=None)
|
||||||
reply_markup=None)
|
|
||||||
await callback.answer()
|
await callback.answer()
|
||||||
|
|
||||||
user_ids_for_broadcast = await user_dal.get_all_active_user_ids_for_broadcast(
|
user_ids = await user_dal.get_all_active_user_ids_for_broadcast(session)
|
||||||
session)
|
|
||||||
|
|
||||||
sent_count = 0
|
sent_count = 0
|
||||||
failed_count = 0
|
failed_count = 0
|
||||||
logging.info(
|
|
||||||
f"Admin {callback.from_user.id} starting broadcast: '{broadcast_message_to_send[:50]}...' to {len(user_ids_for_broadcast)} users."
|
|
||||||
)
|
|
||||||
admin_user = callback.from_user
|
admin_user = callback.from_user
|
||||||
|
logging.info(
|
||||||
|
f"Admin {admin_user.id} broadcasting '{text[:50]}...' to {len(user_ids)} users."
|
||||||
|
)
|
||||||
|
|
||||||
for user_id_to_send in user_ids_for_broadcast:
|
for uid in user_ids:
|
||||||
try:
|
try:
|
||||||
await bot.send_message(user_id_to_send,
|
await bot.send_message(
|
||||||
broadcast_message_to_send,
|
chat_id=uid,
|
||||||
parse_mode="HTML")
|
text=text,
|
||||||
|
entities=entities,
|
||||||
|
)
|
||||||
sent_count += 1
|
sent_count += 1
|
||||||
|
|
||||||
log_payload_sent = {
|
await message_log_dal.create_message_log(
|
||||||
|
session,
|
||||||
|
{
|
||||||
"user_id": admin_user.id,
|
"user_id": admin_user.id,
|
||||||
"telegram_username": admin_user.username,
|
"telegram_username": admin_user.username,
|
||||||
"telegram_first_name": admin_user.first_name,
|
"telegram_first_name": admin_user.first_name,
|
||||||
"event_type": "admin_broadcast_sent",
|
"event_type": "admin_broadcast_sent",
|
||||||
"content":
|
"content": f"To user {uid}: {text[:70]}...",
|
||||||
f"To user {user_id_to_send}: {broadcast_message_to_send[:70]}...",
|
|
||||||
"is_admin_event": True,
|
"is_admin_event": True,
|
||||||
"target_user_id": user_id_to_send
|
"target_user_id": uid,
|
||||||
}
|
},
|
||||||
await message_log_dal.create_message_log(
|
)
|
||||||
session, log_payload_sent)
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
failed_count += 1
|
failed_count += 1
|
||||||
logging.warning(
|
logging.warning(
|
||||||
f"Failed to send broadcast to user {user_id_to_send}: {type(e).__name__} - {e}"
|
f"Failed to send broadcast to {uid}: {type(e).__name__} – {e}"
|
||||||
)
|
)
|
||||||
log_payload_failed = {
|
await message_log_dal.create_message_log(
|
||||||
|
session,
|
||||||
|
{
|
||||||
"user_id": admin_user.id,
|
"user_id": admin_user.id,
|
||||||
"telegram_username": admin_user.username,
|
"telegram_username": admin_user.username,
|
||||||
"telegram_first_name": admin_user.first_name,
|
"telegram_first_name": admin_user.first_name,
|
||||||
"event_type": "admin_broadcast_failed",
|
"event_type": "admin_broadcast_failed",
|
||||||
"content":
|
"content": f"For user {uid}: {type(e).__name__} – {str(e)[:70]}...",
|
||||||
f"For user {user_id_to_send}: {type(e).__name__} - {str(e)[:70]}...",
|
|
||||||
"is_admin_event": True,
|
"is_admin_event": True,
|
||||||
"target_user_id": user_id_to_send
|
"target_user_id": uid,
|
||||||
}
|
},
|
||||||
await message_log_dal.create_message_log(
|
)
|
||||||
session, log_payload_failed)
|
|
||||||
await asyncio.sleep(0.05)
|
await asyncio.sleep(0.05)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@@ -192,17 +217,17 @@ async def confirm_broadcast_callback_handler(callback: types.CallbackQuery,
|
|||||||
await session.rollback()
|
await session.rollback()
|
||||||
logging.error(f"Error committing broadcast logs: {e_commit}")
|
logging.error(f"Error committing broadcast logs: {e_commit}")
|
||||||
|
|
||||||
result_message = _("admin_broadcast_finished_stats",
|
result_message = _("admin_broadcast_finished_stats", sent_count=sent_count, failed_count=failed_count)
|
||||||
sent_count=sent_count,
|
|
||||||
failed_count=failed_count)
|
|
||||||
await callback.message.answer(
|
await callback.message.answer(
|
||||||
result_message,
|
result_message,
|
||||||
reply_markup=get_back_to_admin_panel_keyboard(current_lang, i18n))
|
reply_markup=get_back_to_admin_panel_keyboard(current_lang, i18n),
|
||||||
|
)
|
||||||
|
|
||||||
elif action == "cancel":
|
elif action == "cancel":
|
||||||
await callback.message.edit_text(
|
await callback.message.edit_text(
|
||||||
_("admin_broadcast_cancelled"),
|
_("admin_broadcast_cancelled"),
|
||||||
reply_markup=get_back_to_admin_panel_keyboard(current_lang, i18n))
|
reply_markup=get_back_to_admin_panel_keyboard(current_lang, i18n),
|
||||||
|
)
|
||||||
await callback.answer()
|
await callback.answer()
|
||||||
|
|
||||||
await state.clear()
|
await state.clear()
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import logging
|
import logging
|
||||||
from aiogram import Router, F, types, Bot
|
from aiogram import Router, F, types, Bot
|
||||||
from aiogram.filters import Command
|
from aiogram.filters import Command
|
||||||
|
from aiogram.types import InlineKeyboardButton, InlineKeyboardMarkup
|
||||||
from typing import Optional, Dict, Any, Union
|
from typing import Optional, Dict, Any, Union
|
||||||
from datetime import datetime, timezone
|
from datetime import datetime, timezone
|
||||||
from sqlalchemy.ext.asyncio import AsyncSession
|
from sqlalchemy.ext.asyncio import AsyncSession
|
||||||
@@ -257,97 +258,88 @@ async def reshow_subscription_options_callback(callback: types.CallbackQuery,
|
|||||||
|
|
||||||
|
|
||||||
async def my_subscription_command_handler(
|
async def my_subscription_command_handler(
|
||||||
event: Union[types.Message, types.CallbackQuery], i18n_data: dict,
|
event: Union[types.Message, types.CallbackQuery],
|
||||||
settings: Settings, panel_service: PanelApiService,
|
i18n_data: dict,
|
||||||
subscription_service: SubscriptionService, session: AsyncSession,
|
settings: Settings,
|
||||||
bot: Bot):
|
panel_service: PanelApiService,
|
||||||
target_message_obj = event.message if isinstance(
|
subscription_service: SubscriptionService,
|
||||||
event, types.CallbackQuery) else event
|
session: AsyncSession,
|
||||||
user = event.from_user
|
bot: Bot
|
||||||
|
):
|
||||||
if isinstance(event, types.CallbackQuery):
|
target = event.message if isinstance(event, types.CallbackQuery) else event
|
||||||
await event.answer()
|
|
||||||
|
|
||||||
current_lang = i18n_data.get("current_language", settings.DEFAULT_LANGUAGE)
|
current_lang = i18n_data.get("current_language", settings.DEFAULT_LANGUAGE)
|
||||||
i18n: Optional[JsonI18n] = i18n_data.get("i18n_instance")
|
i18n: JsonI18n = i18n_data.get("i18n_instance")
|
||||||
get_text = lambda key, **kwargs: i18n.gettext(current_lang, key, **kwargs
|
get_text = lambda key, **kw: i18n.gettext(current_lang, key, **kw)
|
||||||
) if i18n else key
|
|
||||||
|
|
||||||
if not i18n or not target_message_obj:
|
if not i18n or not target:
|
||||||
if isinstance(event, types.Message):
|
if isinstance(event, types.Message):
|
||||||
await event.answer(get_text("error_occurred_try_again"))
|
await event.answer(get_text("error_occurred_try_again"))
|
||||||
return
|
return
|
||||||
|
|
||||||
if not panel_service or not subscription_service:
|
if not panel_service or not subscription_service:
|
||||||
logging.error(
|
await target.answer(get_text("error_service_unavailable"))
|
||||||
"PanelService or SubscriptionService is missing in my_subscription_command_handler."
|
|
||||||
)
|
|
||||||
await target_message_obj.answer(get_text("error_service_unavailable"))
|
|
||||||
return
|
return
|
||||||
|
|
||||||
active_sub_details = await subscription_service.get_active_subscription_details(
|
active = await subscription_service.get_active_subscription_details(session, event.from_user.id)
|
||||||
session, user.id)
|
|
||||||
|
|
||||||
sub_info_text_content = ""
|
if not active:
|
||||||
if active_sub_details:
|
text = get_text("subscription_not_active")
|
||||||
end_date_obj = active_sub_details.get('end_date')
|
|
||||||
days_left = 0
|
|
||||||
if end_date_obj:
|
|
||||||
if end_date_obj.tzinfo is None:
|
|
||||||
end_date_obj = end_date_obj.replace(tzinfo=timezone.utc)
|
|
||||||
days_left = (end_date_obj.date() - datetime.now().date()).days
|
|
||||||
|
|
||||||
actual_config_link = active_sub_details.get('config_link') or get_text(
|
buy_button = InlineKeyboardButton(
|
||||||
"config_link_not_available")
|
text=get_text("menu_subscribe_inline", default="Купить"),
|
||||||
|
callback_data="main_action:subscribe"
|
||||||
traffic_limit_bytes = active_sub_details.get('traffic_limit_bytes')
|
|
||||||
traffic_used_bytes = active_sub_details.get('traffic_used_bytes')
|
|
||||||
|
|
||||||
traffic_limit_gb_str = get_text("traffic_unlimited")
|
|
||||||
if traffic_limit_bytes and traffic_limit_bytes > 0:
|
|
||||||
traffic_limit_gb_str = f"{traffic_limit_bytes / (1024**3):.2f} GB"
|
|
||||||
|
|
||||||
traffic_used_gb_str = get_text("traffic_na")
|
|
||||||
if traffic_used_bytes is not None:
|
|
||||||
traffic_used_gb_str = f"{traffic_used_bytes / (1024**3):.2f} GB"
|
|
||||||
|
|
||||||
sub_info_text_content = get_text(
|
|
||||||
"my_subscription_details",
|
|
||||||
end_date=end_date_obj.strftime("%Y-%m-%d")
|
|
||||||
if end_date_obj else "N/A",
|
|
||||||
days_left=max(0, days_left),
|
|
||||||
status=active_sub_details.get(
|
|
||||||
'status_from_panel', get_text('status_active')).capitalize(),
|
|
||||||
config_link=actual_config_link,
|
|
||||||
traffic_limit=traffic_limit_gb_str,
|
|
||||||
traffic_used=traffic_used_gb_str)
|
|
||||||
else:
|
|
||||||
sub_info_text_content = get_text("subscription_not_active")
|
|
||||||
logging.info(
|
|
||||||
f"User {user.id} no active sub details for 'my_subscription'.")
|
|
||||||
|
|
||||||
reply_markup_val = get_back_to_main_menu_markup(current_lang, i18n)
|
|
||||||
|
|
||||||
if isinstance(event, types.CallbackQuery) and event.message:
|
|
||||||
try:
|
|
||||||
await event.message.edit_text(sub_info_text_content,
|
|
||||||
reply_markup=reply_markup_val,
|
|
||||||
parse_mode="HTML",
|
|
||||||
disable_web_page_preview=True)
|
|
||||||
except Exception as e_edit:
|
|
||||||
logging.warning(
|
|
||||||
f"Edit 'my_subscription' failed: {e_edit}. Sending new message to chat {target_message_obj.chat.id}."
|
|
||||||
)
|
)
|
||||||
await bot.send_message(chat_id=target_message_obj.chat.id,
|
back_markup = get_back_to_main_menu_markup(current_lang, i18n)
|
||||||
text=sub_info_text_content,
|
|
||||||
reply_markup=reply_markup_val,
|
kb = InlineKeyboardMarkup(
|
||||||
parse_mode="HTML",
|
inline_keyboard=[
|
||||||
disable_web_page_preview=True)
|
[buy_button],
|
||||||
|
*back_markup.inline_keyboard
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
if isinstance(event, types.CallbackQuery):
|
||||||
|
await event.answer()
|
||||||
|
try:
|
||||||
|
await event.message.edit_text(text, reply_markup=kb)
|
||||||
|
except:
|
||||||
|
await event.message.answer(text, reply_markup=kb)
|
||||||
else:
|
else:
|
||||||
await target_message_obj.answer(sub_info_text_content,
|
await event.answer(text, reply_markup=kb)
|
||||||
reply_markup=reply_markup_val,
|
return
|
||||||
parse_mode="HTML",
|
|
||||||
disable_web_page_preview=True)
|
end_date = active.get("end_date")
|
||||||
|
days_left = (
|
||||||
|
(end_date.date() - datetime.now().date()).days
|
||||||
|
if end_date else 0
|
||||||
|
)
|
||||||
|
text = get_text(
|
||||||
|
"my_subscription_details",
|
||||||
|
end_date=end_date.strftime("%Y-%m-%d") if end_date else "N/A",
|
||||||
|
days_left=max(0, days_left),
|
||||||
|
status=active.get("status_from_panel", get_text("status_active")).capitalize(),
|
||||||
|
config_link=active.get("config_link") or get_text("config_link_not_available"),
|
||||||
|
traffic_limit=(
|
||||||
|
f"{active['traffic_limit_bytes'] / 2**30:.2f} GB"
|
||||||
|
if active.get("traffic_limit_bytes")
|
||||||
|
else get_text("traffic_unlimited")
|
||||||
|
),
|
||||||
|
traffic_used=(
|
||||||
|
f"{active['traffic_used_bytes'] / 2**30:.2f} GB"
|
||||||
|
if active.get("traffic_used_bytes") is not None
|
||||||
|
else get_text("traffic_na")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
markup = get_back_to_main_menu_markup(current_lang, i18n)
|
||||||
|
|
||||||
|
if isinstance(event, types.CallbackQuery):
|
||||||
|
await event.answer()
|
||||||
|
try:
|
||||||
|
await event.message.edit_text(text, reply_markup=markup, parse_mode="HTML", disable_web_page_preview=True)
|
||||||
|
except:
|
||||||
|
await bot.send_message(chat_id=target.chat.id, text=text, reply_markup=markup, parse_mode="HTML", disable_web_page_preview=True)
|
||||||
|
else:
|
||||||
|
await target.answer(text, reply_markup=markup, parse_mode="HTML", disable_web_page_preview=True)
|
||||||
|
|
||||||
|
|
||||||
@router.message(Command("connect"))
|
@router.message(Command("connect"))
|
||||||
|
|||||||
Reference in New Issue
Block a user