mirror of
https://github.com/Gozargah/Marzban.git
synced 2026-05-17 00:25:53 +03:00
* chore: update pydantic to version 2.10.2 and refactor model validators * refactor: simplify field validators by removing unnecessary pre and always flags * remove typing_extensions==4.9.0 from requirements.txt * refactor: remove allow_reuse flag from status field validator * refactor: simplify field validators by removing pre and always flags * refactor: update user model imports and enhance account class with abstract method * refactor: update model_config to use dictionary format in Admin and SubscriptionUserResponse classes * fix typo in UserDataResetByNext * change pre=True to mode="before" * refactor: update validation methods and model configuration in User and Proxy classes * change pre=False with mode="after" * Migrated to Pydantic V2 * fix: custom subscriptions not workong * some small changes * add missing properties to example schema * replace from_orm with model_validate --------- Co-authored-by: MahdiButcher <madibutchercoding@gmail.com> Co-authored-by: Mahdi Butcher <MahdiButcherCoding@gmail.com>
106 lines
4.2 KiB
Python
106 lines
4.2 KiB
Python
import re
|
|
from datetime import datetime as dt
|
|
|
|
from dateutil.relativedelta import relativedelta
|
|
|
|
from app.models.user import User, UserResponse, UserStatus
|
|
from app.models.user_template import UserTemplate
|
|
from app.utils.system import readable_size
|
|
|
|
statuses = {
|
|
UserStatus.active: "✅",
|
|
UserStatus.expired: "🕰",
|
|
UserStatus.limited: "🪫",
|
|
UserStatus.disabled: "❌",
|
|
UserStatus.on_hold: "🔌",
|
|
}
|
|
|
|
|
|
def time_to_string(time: dt):
|
|
now = dt.now()
|
|
if time < now:
|
|
delta = now - time
|
|
days = delta.days
|
|
hours, remainder = divmod(delta.seconds, 3600)
|
|
minutes, _ = divmod(remainder, 60)
|
|
if days > 0:
|
|
return f"about <code>{days}</code> days ago"
|
|
elif hours > 0:
|
|
return f"about <code>{hours}</code> hours ago"
|
|
elif minutes > 0:
|
|
return f"about <code>{minutes}</code> minutes ago"
|
|
else:
|
|
return "just now"
|
|
else:
|
|
delta = time - now
|
|
days = delta.days
|
|
hours, remainder = divmod(delta.seconds, 3600)
|
|
minutes, _ = divmod(remainder, 60)
|
|
if days > 0:
|
|
return f"in about <code>{days}</code> days"
|
|
elif hours > 0:
|
|
return f"in about <code>{hours}</code> hours"
|
|
elif minutes > 0:
|
|
return f"in about <code>{minutes}</code> minutes"
|
|
else:
|
|
return "very soon"
|
|
|
|
|
|
def get_user_info_text(db_user: User) -> str:
|
|
user: UserResponse = UserResponse.model_validate(db_user)
|
|
data_limit = readable_size(user.data_limit) if user.data_limit else "Unlimited"
|
|
used_traffic = readable_size(user.used_traffic) if user.used_traffic else "-"
|
|
data_left = readable_size(user.data_limit - user.used_traffic) if user.data_limit else "-"
|
|
on_hold_timeout = user.on_hold_timeout.strftime("%Y-%m-%d") if user.on_hold_timeout else "-"
|
|
on_hold_duration = user.on_hold_expire_duration // (24*60*60) if user.on_hold_expire_duration else None
|
|
expiry_date = dt.fromtimestamp(user.expire).date() if user.expire else "Never"
|
|
time_left = time_to_string(dt.fromtimestamp(user.expire)) if user.expire else "-"
|
|
online_at = time_to_string(user.online_at) if user.online_at else "-"
|
|
sub_updated_at = time_to_string(user.sub_updated_at) if user.sub_updated_at else "-"
|
|
if user.status == UserStatus.on_hold:
|
|
expiry_text = f"⏰ <b>On Hold Duration:</b> <code>{on_hold_duration} days</code> (auto start at <code>{
|
|
on_hold_timeout}</code>)"
|
|
else:
|
|
expiry_text = f"📅 <b>Expiry Date:</b> <code>{expiry_date}</code> ({time_left})"
|
|
return f"""\
|
|
{statuses[user.status]} <b>Status:</b> <code>{user.status.title()}</code>
|
|
|
|
🔤 <b>Username:</b> <code>{user.username}</code>
|
|
|
|
🔋 <b>Data limit:</b> <code>{data_limit}</code>
|
|
📶 <b>Data Used:</b> <code>{used_traffic}</code> (<code>{data_left}</code> left)
|
|
{expiry_text}
|
|
|
|
🔌 <b>Online at:</b> {online_at}
|
|
🔄 <b>Subscription updated at:</b> {sub_updated_at}
|
|
📱 <b>Subscription last agent:</b> <blockquote>{user.sub_last_user_agent or "-"}</blockquote>
|
|
|
|
📝 <b>Note:</b> <blockquote expandable>{user.note or "empty"}</blockquote>
|
|
👨💻 <b>Admin:</b> <code>{db_user.admin.username if db_user.admin else "-"}</code>
|
|
🚀 <b><a href="{user.subscription_url}">Subscription</a>:</b> <code>{user.subscription_url}</code>"""
|
|
|
|
|
|
def get_template_info_text(template: UserTemplate):
|
|
protocols = ""
|
|
for p, inbounds in template.inbounds.items():
|
|
protocols += f"\n├─ <b>{p.upper()}</b>\n"
|
|
protocols += "├───" + ", ".join([f"<code>{i}</code>" for i in inbounds])
|
|
data_limit = readable_size(template.data_limit) if template.data_limit else "Unlimited"
|
|
expire = ((dt.now() + relativedelta(seconds=template.expire_duration))
|
|
.strftime("%Y-%m-%d")) if template.expire_duration else "Never"
|
|
text = f"""
|
|
📊 Template Info:
|
|
ID: <b>{template.id}</b>
|
|
Data Limit: <b>{data_limit}</b>
|
|
Expire Date: <b>{expire}</b>
|
|
Username Prefix: <b>{template.username_prefix if template.username_prefix else "-"}</b>
|
|
Username Suffix: <b>{template.username_suffix if template.username_suffix else "-"}</b>
|
|
Protocols: {protocols}"""
|
|
return text
|
|
|
|
|
|
def get_number_at_end(username: str):
|
|
n = re.search(r'(\d+)$', username)
|
|
if n:
|
|
return n.group(1)
|