diff --git a/app/db/crud.py b/app/db/crud.py
index f551569..54ad403 100644
--- a/app/db/crud.py
+++ b/app/db/crud.py
@@ -502,7 +502,7 @@ def create_service(db: Session, service: Service) -> Service:
return dbservice
-def get_service(db: Session, service_name) -> Service:
+def get_service(db: Session, service_name: str) -> Service:
return db.query(Service).filter( Service.name == service_name ).first()
def get_services(db: Session) -> List[Service]:
diff --git a/app/models/user.py b/app/models/user.py
index ddf650a..41919a2 100644
--- a/app/models/user.py
+++ b/app/models/user.py
@@ -139,9 +139,9 @@ class UserCreate(User):
class UserModify(User):
- status: UserStatusModify = None
- services: List[int] = []
- data_limit_reset_strategy: UserDataLimitResetStrategy = None
+ status: Optional[UserStatusModify] = Field(None)
+ services: List[int] = Field([])
+ data_limit_reset_strategy: Optional[UserDataLimitResetStrategy] = Field(None)
model_config = ConfigDict(json_schema_extra={
"example": {
"username": "mammad1234",
@@ -179,10 +179,11 @@ class UserResponse(User):
created_at: datetime
inbounds: List[InboundBase] = Field([])
services: List[ServiceBase] = Field([])
+
# links: List[str] = [] # Field(default_factory=lamba x: )
# subscription_url: str = ""
model_config = ConfigDict(from_attributes=True)
-
+
@computed_field
@property
def links(self) -> List[str]:
diff --git a/app/telegram/handlers/admin.py b/app/telegram/handlers/admin.py
index f0c10c2..ebb92c3 100644
--- a/app/telegram/handlers/admin.py
+++ b/app/telegram/handlers/admin.py
@@ -17,11 +17,10 @@ from app import xray
from app.db import GetDB, crud
from app.models.user import (UserCreate, UserModify, UserResponse, UserStatus,
UserStatusModify)
-# from app.models.user_template import UserTemplateResponse
from app.models.proxy import ProxyTypes
from app.telegram import bot
-from app.telegram.utils.custom_filters import (cb_query_equals,
- cb_query_startswith)
+from app.telegram.utils.custom_filters import (query_equals,
+ query_startswith)
from app.telegram.utils.keyboard import BotKeyboard
from app.utils.store import MemoryStorage
from app.utils.system import cpu_usage, memory_usage, readable_size
@@ -42,7 +41,7 @@ class botStates(StatesGroup):
edit_user_data_limit = State()
edit_user_expire = State()
edit_note = State()
- add_user_from_template = State()
+ # add_user_from_template = State()
add_user_username = State()
add_user_data_limit = State()
add_user_expire = State()
@@ -119,7 +118,7 @@ To get started, use the buttons below.
), parse_mode="html", reply_markup=BotKeyboard.main_menu())
-@bot.callback_query_handler(cb_query_equals('system'), is_admin=True)
+@bot.callback_query_handler(query_equals('system'), is_admin=True)
async def system_command(call: types.CallbackQuery):
return await bot.edit_message_text(
get_system_info(),
@@ -130,17 +129,17 @@ async def system_command(call: types.CallbackQuery):
)
-@bot.callback_query_handler(cb_query_equals('restart'), is_admin=True)
+@bot.callback_query_handler(query_equals('restart'), is_admin=True)
async def restart_command(call: types.CallbackQuery):
await bot.edit_message_text(
'⚠️ Are you sure? This will restart Xray core.',
call.message.chat.id,
call.message.message_id,
- reply_markup=BotKeyboard.confirm_action(action='restart')
+ reply_markup=BotKeyboard.confirm_action(action='do_restart')
)
-@bot.callback_query_handler(cb_query_startswith('delete:'), is_admin=True)
+@bot.callback_query_handler(query_startswith('delete:'), is_admin=True)
async def delete_user_command(call: types.CallbackQuery):
username = call.data.split(':')[1]
await bot.edit_message_text(
@@ -149,11 +148,11 @@ async def delete_user_command(call: types.CallbackQuery):
call.message.message_id,
parse_mode="markdown",
reply_markup=BotKeyboard.confirm_action(
- action='delete', username=username)
+ action='do_delete', username=username)
)
-@bot.callback_query_handler(cb_query_startswith("suspend:"), is_admin=True)
+@bot.callback_query_handler(query_startswith("suspend:"), is_admin=True)
async def suspend_user_command(call: types.CallbackQuery):
username = call.data.split(":")[1]
await bot.edit_message_text(
@@ -162,11 +161,11 @@ async def suspend_user_command(call: types.CallbackQuery):
call.message.message_id,
parse_mode="markdown",
reply_markup=BotKeyboard.confirm_action(
- action="suspend", username=username),
+ action="do_suspend", username=username),
)
-@bot.callback_query_handler(cb_query_startswith("activate:"), is_admin=True)
+@bot.callback_query_handler(query_startswith("activate:"), is_admin=True)
async def activate_user_command(call: types.CallbackQuery):
username = call.data.split(":")[1]
await bot.edit_message_text(
@@ -175,11 +174,11 @@ async def activate_user_command(call: types.CallbackQuery):
call.message.message_id,
parse_mode="markdown",
reply_markup=BotKeyboard.confirm_action(
- action="activate", username=username),
+ action="do_activate", username=username),
)
-@bot.callback_query_handler(cb_query_startswith("reset_usage:"), is_admin=True)
+@bot.callback_query_handler(query_startswith("reset_usage:"), is_admin=True)
async def reset_usage_user_command(call: types.CallbackQuery):
username = call.data.split(":")[1]
await bot.edit_message_text(
@@ -188,11 +187,11 @@ async def reset_usage_user_command(call: types.CallbackQuery):
call.message.message_id,
parse_mode="markdown",
reply_markup=BotKeyboard.confirm_action(
- action="reset_usage", username=username),
+ action="do_reset_usage", username=username),
)
-@bot.callback_query_handler(cb_query_equals('edit_all'), is_admin=True)
+@bot.callback_query_handler(query_equals('edit_all'), is_admin=True)
async def edit_all_command(call: types.CallbackQuery):
with GetDB() as db:
total_users = crud.get_users_count(db)
@@ -215,27 +214,27 @@ async def edit_all_command(call: types.CallbackQuery):
)
-@bot.callback_query_handler(cb_query_equals('delete_expired'), is_admin=True)
+@bot.callback_query_handler(query_equals('delete_expired'), is_admin=True)
async def delete_expired_command(call: types.CallbackQuery):
await bot.edit_message_text(
f"⚠️ Are you sure? This will *DELETE All Expired Users*‼️",
call.message.chat.id,
call.message.message_id,
parse_mode="markdown",
- reply_markup=BotKeyboard.confirm_action(action="delete_expired"))
+ reply_markup=BotKeyboard.confirm_action(action="do_delete_expired"))
-@bot.callback_query_handler(cb_query_equals('delete_limited'), is_admin=True)
+@bot.callback_query_handler(query_equals('delete_limited'), is_admin=True)
async def delete_limited_command(call: types.CallbackQuery):
await bot.edit_message_text(
f"⚠️ Are you sure? This will *DELETE All Limited Users*‼️",
call.message.chat.id,
call.message.message_id,
parse_mode="markdown",
- reply_markup=BotKeyboard.confirm_action(action="delete_limited"))
+ reply_markup=BotKeyboard.confirm_action(action="do_delete_limited"))
-@bot.callback_query_handler(cb_query_equals('add_data'), is_admin=True)
+@bot.callback_query_handler(query_equals('add_data'), is_admin=True)
async def add_data_command(call: types.CallbackQuery):
msg = await bot.edit_message_text(
f"🔋 Enter Data Limit to increase or decrease (GB):",
@@ -264,13 +263,13 @@ async def add_data_step(message):
f"⚠️ Are you sure? this will change Data limit of all users according to "
f"{'+' if data_limit > 0 else '-'}{readable_size(abs(data_limit *1024*1024*1024))}",
parse_mode="html",
- reply_markup=BotKeyboard.confirm_action('add_data', data_limit))
+ reply_markup=BotKeyboard.confirm_action('do_add_data', data_limit))
await bot.delete_state(message.from_user.id, message.chat.id)
await cleanup_messages(message.chat.id)
schedule_delete_message(message.chat.id, msg.id)
-@bot.callback_query_handler(cb_query_equals('add_time'), is_admin=True)
+@bot.callback_query_handler(query_equals('add_time'), is_admin=True)
async def add_time_command(call: types.CallbackQuery):
msg = await bot.edit_message_text(
f"📅 Enter Days to increase or decrease expiry:",
@@ -298,12 +297,12 @@ async def add_time_step(message):
message.chat.id,
f"⚠️ Are you sure? this will change Expiry Time of all users according to {days} Days",
parse_mode="html",
- reply_markup=BotKeyboard.confirm_action('add_time', days))
+ reply_markup=BotKeyboard.confirm_action('do_add_time', days))
await cleanup_messages(message.chat.id)
schedule_delete_message(message.chat.id, msg.id)
-@bot.callback_query_handler(cb_query_startswith("inbound"), is_admin=True)
+@bot.callback_query_handler(query_startswith("inbound"), is_admin=True)
async def inbound_command(call: types.CallbackQuery):
await bot.edit_message_text(
f"Select inbound to *{call.data[8:].title()}* from all users",
@@ -313,7 +312,7 @@ async def inbound_command(call: types.CallbackQuery):
reply_markup=BotKeyboard.inbounds_menu(call.data, xray.config.inbounds_by_tag))
-@bot.callback_query_handler(cb_query_startswith("confirm_inbound"), is_admin=True)
+@bot.callback_query_handler(query_startswith("confirm_inbound"), is_admin=True)
async def delete_expired_confirm_command(call: types.CallbackQuery):
await bot.edit_message_text(
f"⚠️ Are you sure? This will *{call.data[16:].replace(':', ' ')} for All Users*‼️",
@@ -323,7 +322,7 @@ async def delete_expired_confirm_command(call: types.CallbackQuery):
reply_markup=BotKeyboard.confirm_action(action=call.data[8:]))
-@bot.callback_query_handler(cb_query_startswith("edit:"), is_admin=True)
+@bot.callback_query_handler(query_startswith("edit:"), is_admin=True)
async def edit_command(call: types.CallbackQuery):
# bot.clear_step_handler_by_chat_id(call.message.chat.id)
await bot.delete_state(call.from_user.id, call.message.chat.id)
@@ -336,18 +335,20 @@ async def edit_command(call: types.CallbackQuery):
'❌ User not found.',
show_alert=True
)
+ services = crud.get_services(db)
user = UserResponse.model_validate(db_user)
mem_store.set(f'{call.message.chat.id}:username', username)
mem_store.set(f'{call.message.chat.id}:data_limit', db_user.data_limit)
mem_store.set(f'{call.message.chat.id}:expire_date', datetime.fromtimestamp(db_user.expire) if db_user.expire else None)
- mem_store.set(f'{call.message.chat.id}:protocols', {protocol.value: inbounds for protocol, inbounds in db_user.inbounds.items()})
+ mem_store.set(f'{call.message.chat.id}:services', [s.id for s in db_user.services])
await bot.edit_message_text(
f"📝 Editing user `{username}`",
call.message.chat.id,
call.message.message_id,
parse_mode="markdown",
- reply_markup=BotKeyboard.select_protocols(
- user.inbounds,
+ reply_markup=BotKeyboard.select_services(
+ services,
+ [s.id for s in user.services],
"edit",
username=username,
data_limit=db_user.data_limit,
@@ -356,7 +357,7 @@ async def edit_command(call: types.CallbackQuery):
)
-@bot.callback_query_handler(cb_query_equals('help_edit'), is_admin=True)
+@bot.callback_query_handler(query_equals('help_edit'), is_admin=True)
async def help_edit_command(call: types.CallbackQuery):
await bot.answer_callback_query(
call.id,
@@ -365,7 +366,7 @@ async def help_edit_command(call: types.CallbackQuery):
)
-@bot.callback_query_handler(cb_query_equals('cancel'), is_admin=True)
+@bot.callback_query_handler(query_equals('cancel'), is_admin=True)
async def cancel_command(call: types.CallbackQuery):
# bot.clear_step_handler_by_chat_id(call.message.chat.id)
await bot.delete_state(call.from_user.id, call.message.chat.id)
@@ -378,7 +379,7 @@ async def cancel_command(call: types.CallbackQuery):
)
-@bot.callback_query_handler(cb_query_startswith('edit_user:'), is_admin=True)
+@bot.callback_query_handler(query_startswith('edit_user:'), is_admin=True)
async def edit_user_command(call: types.CallbackQuery):
_, username, action = call.data.split(":")
schedule_delete_message(call.message.chat.id, call.message.id)
@@ -494,7 +495,7 @@ async def edit_user_expire_step(message: types.Message):
await cleanup_messages(message.chat.id)
-@bot.callback_query_handler(cb_query_startswith('users:'), is_admin=True)
+@bot.callback_query_handler(query_startswith('users:'), is_admin=True)
async def users_command(call: types.CallbackQuery):
page = int(call.data.split(':')[1]) if len(call.data.split(':')) > 1 else 1
with GetDB() as db:
@@ -541,25 +542,8 @@ def get_user_info_text(
return text
-def get_template_info_text(
- id: int, data_limit: int, expire_duration: int, username_prefix: str, username_suffix: str, inbounds: dict):
- protocols = ""
- for p, inbounds in inbounds.items():
- protocols += f"\n├─ {p.upper()}\n"
- protocols += "├───" + ", ".join([f"{i}" for i in inbounds])
- text = f"""
-📊 Template Info:
-┌ ID: {id}
-├ Data Limit: {readable_size(data_limit) if data_limit else 'Unlimited'}
-├ Expire Date: {(datetime.now() + relativedelta(seconds=expire_duration)).strftime('%Y-%m-%d') if expire_duration else 'Never'}
-├ Username Prefix: {username_prefix if username_prefix else '🚫'}
-├ Username Suffix: {username_suffix if username_suffix else '🚫'}
-├ Protocols: {protocols}
- """
- return text
-
-@bot.callback_query_handler(cb_query_startswith('edit_note:'), is_admin=True)
+@bot.callback_query_handler(query_startswith('edit_note:'), is_admin=True)
async def edit_note_command(call: types.CallbackQuery):
username = call.data.split(':')[1]
with GetDB() as db:
@@ -593,7 +577,7 @@ async def edit_note_step(message: types.Message):
with GetDB() as db:
db_user = crud.get_user(db, username)
last_note = db_user.note
- db_user = crud.update_user(db, db_user, UserModify(note=note))
+ db_user = crud.update_user(db, db_user, UserModify(username=username, note=note))
user = UserResponse.model_validate(db_user)
text = get_user_info_text(
status=user.status,
@@ -622,7 +606,7 @@ async def edit_note_step(message: types.Message):
-@bot.callback_query_handler(cb_query_startswith('user:'), is_admin=True)
+@bot.callback_query_handler(query_startswith('user:'), is_admin=True)
async def user_command(call: types.CallbackQuery):
await bot.delete_state(call.from_user.id, call.message.chat.id)
# bot.clear_step_handler_by_chat_id(call.message.chat.id)
@@ -648,7 +632,7 @@ async def user_command(call: types.CallbackQuery):
page=page, note=True))
-@bot.callback_query_handler(cb_query_startswith("revoke_sub:"), is_admin=True)
+@bot.callback_query_handler(query_startswith("revoke_sub:"), is_admin=True)
async def revoke_sub_command(call: types.CallbackQuery):
username = call.data.split(":")[1]
await bot.edit_message_text(
@@ -656,10 +640,10 @@ async def revoke_sub_command(call: types.CallbackQuery):
call.message.chat.id,
call.message.message_id,
parse_mode="markdown",
- reply_markup=BotKeyboard.confirm_action(action=call.data))
+ reply_markup=BotKeyboard.confirm_action(action="do_revoke", username=username))
-@bot.callback_query_handler(cb_query_startswith("links:"), is_admin=True)
+@bot.callback_query_handler(query_startswith("links:"), is_admin=True)
async def links_command(call: types.CallbackQuery):
username = call.data.split(":")[1]
@@ -683,7 +667,7 @@ async def links_command(call: types.CallbackQuery):
)
-@bot.callback_query_handler(cb_query_startswith("genqr:"), is_admin=True)
+@bot.callback_query_handler(query_startswith("genqr:"), is_admin=True)
async def genqr_command(call: types.CallbackQuery):
username = call.data.split(":")[1]
@@ -743,193 +727,7 @@ async def genqr_command(call: types.CallbackQuery):
reply_markup=BotKeyboard.show_links(username)
)
-
-@bot.callback_query_handler(cb_query_startswith('template_charge:'), is_admin=True)
-async def template_charge_command(call: types.CallbackQuery):
- _, template_id, username = call.data.split(":")
- now = datetime.now()
- today = datetime(
- year=now.year,
- month=now.month,
- day=now.day,
- hour=23,
- minute=59,
- second=59
- )
- with GetDB() as db:
- template = crud.get_user_template(db, template_id)
- if not template:
- return await bot.answer_callback_query(call.id, "Template not found!", show_alert=True)
- template = UserTemplateResponse.model_validate(template)
-
- db_user = crud.get_user(db, username)
- if not db_user:
- return await bot.answer_callback_query(call.id, "User not found!", show_alert=True)
- user = UserResponse.model_validate(db_user)
- if (user.data_limit and not user.expire) or (not user.data_limit and user.expire):
- text = get_user_info_text(
- status='active',
- username=username,
- sub_url=user.subscription_url,
- expire=int(((datetime.fromtimestamp(user.expire) if user.expire else today) +
- relativedelta(seconds=template.expire_duration)).timestamp()),
- data_limit=(
- user.data_limit - user.used_traffic + template.data_limit) if user.data_limit else template.data_limit,
- usage=0, note=user.note)
- await bot.edit_message_text(f'''\
-‼️ If add template Bandwidth and Time to the user, the user will be this:\n\n\
-{text}\n\n\
-Add template Bandwidth and Time to user or Reset to Template default⁉️''',
- call.message.chat.id, call.message.message_id, parse_mode='html',
- reply_markup=BotKeyboard.charge_add_or_reset(
- username=username, template_id=template_id))
- elif (not user.data_limit and not user.expire) or (user.used_traffic > user.data_limit) or (now > datetime.fromtimestamp(user.expire)):
- crud.reset_user_data_usage(db, db_user)
- expire_date = None
- if template.expire_duration:
- expire_date = today + relativedelta(seconds=template.expire_duration)
- modify = UserModify(
- status=UserStatusModify.active,
- expire=int(expire_date.timestamp()) if expire_date else 0,
- data_limit=template.data_limit,
- )
- db_user = crud.update_user(db, db_user, modify)
- await xray.operations.add_user(db_user)
-
- text = get_user_info_text(
- status='active',
- username=username,
- sub_url=user.subscription_url,
- expire=int(expire_date.timestamp()),
- data_limit=template.data_limit,
- usage=0, note=user.note)
- await bot.edit_message_text(
- f'🔋 User Successfully Charged!\n\n{text}',
- call.message.chat.id,
- call.message.message_id,
- parse_mode='html',
- reply_markup=BotKeyboard.user_menu(user_info={
- 'status': 'active',
- 'username': user.username}, note=True))
- if TELEGRAM_LOGGER_CHANNEL_ID:
- text = f'''\
-🔋 #Charged #Reset #From_Bot
-➖➖➖➖➖➖➖➖➖
-Template : {template.name}
-Username : {user.username}
-➖➖➖➖➖➖➖➖➖
-Last status
-├Traffic Limit : {readable_size(user.data_limit) if user.data_limit else "Unlimited"}
-├Expire Date : \
-{datetime.fromtimestamp(user.expire).strftime('%H:%M:%S %Y-%m-%d') if user.expire else "Never"}
-➖➖➖➖➖➖➖➖➖
-New status
-├Traffic Limit : {readable_size(db_user.data_limit) if db_user.data_limit else "Unlimited"}
-├Expire Date : \
-{datetime.fromtimestamp(db_user.expire).strftime('%H:%M:%S %Y-%m-%d') if db_user.expire else "Never"}
-➖➖➖➖➖➖➖➖➖
-By : {call.from_user.full_name}'''
- try:
- await bot.send_message(TELEGRAM_LOGGER_CHANNEL_ID, text, 'HTML')
- except:
- pass
- else:
- text = get_user_info_text(
- status='active',
- username=username,
- sub_url=user.subscription_url,
- expire=int(((datetime.fromtimestamp(user.expire) if user.expire else today) +
- relativedelta(seconds=template.expire_duration)).timestamp()),
- data_limit=(
- user.data_limit - user.used_traffic + template.data_limit) if user.data_limit else template.data_limit,
- usage=0, note=user.note)
- await bot.edit_message_text(f'''\
-‼️ If add template Bandwidth and Time to the user, the user will be this:\n\n\
-{text}\n\n\
-Add template Bandwidth and Time to user or Reset to Template default⁉️''',
- call.message.chat.id, call.message.message_id, parse_mode='html',
- reply_markup=BotKeyboard.charge_add_or_reset(
- username=username, template_id=template_id))
-
-
-@bot.callback_query_handler(cb_query_startswith('charge:'), is_admin=True)
-async def charge_command(call: types.CallbackQuery):
- username = call.data.split(":")[1]
- with GetDB() as db:
- templates = crud.get_user_templates(db)
- if not templates:
- return await bot.answer_callback_query(call.id, "You don't have any User Templates!")
-
- db_user = crud.get_user(db, username)
- if not db_user:
- return await bot.answer_callback_query(call.id, "User not found!", show_alert=True)
-
- await bot.edit_message_text(
- f"{call.message.html_text}\n\n🔢 Select User Template to charge:",
- call.message.chat.id,
- call.message.message_id,
- parse_mode='html',
- reply_markup=BotKeyboard.templates_menu(
- {template.name: template.id for template in templates},
- username=username,
- )
- )
-
-
-@bot.callback_query_handler(cb_query_equals('template_add_user'), is_admin=True)
-async def add_user_from_template_command(call: types.CallbackQuery):
- with GetDB() as db:
- templates = crud.get_user_templates(db)
- if not templates:
- return await bot.answer_callback_query(call.id, "You don't have any User Templates!")
-
- await bot.edit_message_text(
- "Select a Template to create user from:",
- call.message.chat.id,
- call.message.message_id,
- parse_mode='html',
- reply_markup=BotKeyboard.templates_menu({template.name: template.id for template in templates})
- )
-
-
-@bot.callback_query_handler(cb_query_startswith('template_add_user:'), is_admin=True)
-async def add_user_from_template(call: types.CallbackQuery):
- template_id = int(call.data.split(":")[1])
- with GetDB() as db:
- template = crud.get_user_template(db, template_id)
- if not template:
- return await bot.answer_callback_query(call.id, "Template not found!", show_alert=True)
- template = UserTemplateResponse.model_validate(template)
-
- text = get_template_info_text(
- template_id, data_limit=template.data_limit, expire_duration=template.expire_duration,
- username_prefix=template.username_prefix, username_suffix=template.username_suffix,
- inbounds=template.inbounds)
- if template.username_prefix:
- text += f"\n⚠️ Username will be prefixed with {template.username_prefix}"
- if template.username_suffix:
- text += f"\n⚠️ Username will be suffixed with {template.username_suffix}"
-
- mem_store.set(f"{call.message.chat.id}:template_id", template.id)
- template_msg = await bot.edit_message_text(
- text,
- call.message.chat.id,
- call.message.message_id,
- parse_mode="HTML"
- )
- text = '👤 Enter username:\n⚠️ Username only can be 3 to 32 characters and contain a-z, A-Z, 0-9, and underscores in between.'
- msg = await bot.send_message(
- call.message.chat.id,
- text,
- parse_mode="HTML",
- reply_markup=BotKeyboard.random_username(template_id=template.id)
- )
- schedule_delete_message(call.message.chat.id, template_msg.message_id, msg.id)
- await bot.set_state(call.from_user.id, botStates.add_user_from_template, call.message.chat.id)
- # bot.register_next_step_handler(template_msg, add_user_from_template_username_step)
-
-
-@bot.callback_query_handler(cb_query_startswith('random'), is_admin=True)
+@bot.callback_query_handler(query_startswith('random'), is_admin=True)
async def random_username(call: types.CallbackQuery):
# bot.clear_step_handler_by_chat_id(call.message.chat.id)
await bot.delete_state(call.from_user.id, call.message.chat.id)
@@ -942,141 +740,17 @@ async def random_username(call: types.CallbackQuery):
schedule_delete_message(call.message.chat.id, call.message.id)
await cleanup_messages(call.message.chat.id)
- if not template_id:
- msg = await bot.send_message(call.message.chat.id,
- '⬆️ Enter Data Limit (GB):\n⚠️ Send 0 for unlimited.',
- reply_markup=BotKeyboard.inline_cancel_action())
- schedule_delete_message(call.message.chat.id, msg.id)
- await bot.set_state(call.from_user.id, botStates.add_user_data_limit, call.message.chat.id)
- async with bot.retrieve_data(call.from_user.id, call.message.chat.id) as data:
- data["username"] = username
- return # bot.register_next_step_handler(call.message, add_user_data_limit_step, username=username)
-
- with GetDB() as db:
- template = crud.get_user_template(db, template_id)
- if template.username_prefix:
- username = template.username_prefix + username
- if template.username_suffix:
- username += template.username_suffix
-
- template = UserTemplateResponse.model_validate(template)
+ msg = await bot.send_message(call.message.chat.id,
+ '⬆️ Enter Data Limit (GB):\n⚠️ Send 0 for unlimited.',
+ reply_markup=BotKeyboard.inline_cancel_action())
+ schedule_delete_message(call.message.chat.id, msg.id)
await bot.set_state(call.from_user.id, botStates.add_user_data_limit, call.message.chat.id)
async with bot.retrieve_data(call.from_user.id, call.message.chat.id) as data:
- data["username"] = username
- mem_store.set(f"{call.message.chat.id}:username", username)
- mem_store.set(f"{call.message.chat.id}:data_limit", template.data_limit)
- mem_store.set(f"{call.message.chat.id}:protocols", template.inbounds)
- now = datetime.now()
- today = datetime(
- year=now.year,
- month=now.month,
- day=now.day,
- hour=23,
- minute=59,
- second=59)
- expire_date = None
- if template.expire_duration:
- expire_date = today + relativedelta(seconds=template.expire_duration)
- mem_store.set(f"{call.message.chat.id}:expire_date", expire_date)
-
- text = f"📝 Creating user {username}\n" + get_template_info_text(
- id=template.id, data_limit=template.data_limit, expire_duration=template.expire_duration,
- username_prefix=template.username_prefix, username_suffix=template.username_suffix, inbounds=template.inbounds)
-
- await bot.send_message(
- call.message.chat.id,
- text,
- parse_mode="HTML",
- reply_markup=BotKeyboard.select_protocols(
- template.inbounds,
- "create_from_template",
- username=username,
- data_limit=template.data_limit,
- expire_date=expire_date,))
-
-@bot.message_handler(state=botStates.add_user_from_template)
-async def add_user_from_template_username_step(message: types.Message):
- template_id = mem_store.get(f"{message.chat.id}:template_id")
- if template_id is None:
- return await bot.send_message(message.chat.id, "An error occured in the process! try again.")
-
- if not message.text:
- wait_msg = await bot.send_message(message.chat.id, '❌ Username can not be empty.')
- schedule_delete_message(message.chat.id, wait_msg.message_id, message.message_id)
- return # bot.register_next_step_handler(wait_msg, add_user_from_template_username_step)
-
- with GetDB() as db:
- username = message.text
-
- template = crud.get_user_template(db, template_id)
- if template.username_prefix:
- username = template.username_prefix + username
- if template.username_suffix:
- username += template.username_suffix
-
- match = re.match(r"^(?=\w{3,32}\b)[a-zA-Z0-9-_@.]+(?:_[a-zA-Z0-9-_@.]+)*$", username)
- if not match:
- wait_msg = await bot.send_message(message.chat.id,
- '❌ Username only can be 3 to 32 characters and contain a-z, A-Z, 0-9, and underscores in between.')
- schedule_delete_message(message.chat.id, wait_msg.message_id, message.message_id)
- return # bot.register_next_step_handler(wait_msg, add_user_from_template_username_step)
-
- if len(username) < 3:
- wait_msg = await bot.send_message(message.chat.id,
- f"❌ Username can't be generated because is shorter than 32 characters! username: {username}",
- parse_mode="HTML")
- schedule_delete_message(message.chat.id, wait_msg.message_id, message.message_id)
- return # bot.register_next_step_handler(wait_msg, add_user_from_template_username_step)
- elif len(username) > 32:
- wait_msg = await bot.send_message(message.chat.id,
- f"❌ Username can't be generated because is longer than 32 characters! username: {username}",
- parse_mode="HTML")
- schedule_delete_message(message.chat.id, wait_msg.message_id, message.message_id)
- return # bot.register_next_step_handler(wait_msg, add_user_from_template_username_step)
-
- if crud.get_user(db, username):
- wait_msg = await bot.send_message(message.chat.id, '❌ Username already exists.')
- schedule_delete_message(message.chat.id, wait_msg.message_id, message.message_id)
- return # bot.register_next_step_handler(wait_msg, add_user_from_template_username_step)
- template = UserTemplateResponse.model_validate(template)
- mem_store.set(f"{message.chat.id}:username", username)
- mem_store.set(f"{message.chat.id}:data_limit", template.data_limit)
- mem_store.set(f"{message.chat.id}:protocols", template.inbounds)
- now = datetime.now()
- today = datetime(
- year=now.year,
- month=now.month,
- day=now.day,
- hour=23,
- minute=59,
- second=59
- )
- expire_date = None
- if template.expire_duration:
- expire_date = today + relativedelta(seconds=template.expire_duration)
- mem_store.set(f"{message.chat.id}:expire_date", expire_date)
-
- text = f"📝 Creating user {username}\n" + get_template_info_text(
- id=template.id, data_limit=template.data_limit, expire_duration=template.expire_duration,
- username_prefix=template.username_prefix, username_suffix=template.username_suffix, inbounds=template.inbounds)
-
- await bot.send_message(
- message.chat.id,
- text,
- parse_mode="HTML",
- reply_markup=BotKeyboard.select_protocols(
- template.inbounds,
- "create_from_template",
- username=username,
- data_limit=template.data_limit,
- expire_date=expire_date,
- )
- )
- schedule_delete_message(message.chat.id, message.id)
- await cleanup_messages(message.chat.id)
+ data["username"] = username
+ return
-@bot.callback_query_handler(cb_query_equals('add_user'), is_admin=True)
+@bot.callback_query_handler(query_equals('add_user'), is_admin=True)
async def add_user_command(call: types.CallbackQuery):
try:
await bot.delete_message(call.message.chat.id, call.message.message_id)
@@ -1192,80 +866,48 @@ async def add_user_expire_step(message: types.Message):
mem_store.set(f'{message.chat.id}:data_limit', data_limit)
mem_store.set(f'{message.chat.id}:expire_date', expire_date)
+ with GetDB() as db:
+ services = crud.get_services(db)
schedule_delete_message(message.chat.id, message.id)
await cleanup_messages(message.chat.id)
await bot.send_message(
message.chat.id,
- 'Select Protocols:\nUsernames: {}\nData Limit: {}\nExpiry Date {}'.format(
+ 'Select Services:\nUsernames: {}\nData Limit: {}\nExpiry Date {}'.format(
mem_store.get(f'{message.chat.id}:username'),
readable_size(mem_store.get(f'{message.chat.id}:data_limit'))
if mem_store.get(f'{message.chat.id}:data_limit') else "Unlimited",
mem_store.get(f'{message.chat.id}:expire_date').strftime("%Y-%m-%d")
if mem_store.get(f'{message.chat.id}:expire_date') else 'Never'
),
- reply_markup=BotKeyboard.select_protocols({}, action="create")
+ reply_markup=BotKeyboard.select_services(services, [], action="create")
)
-@bot.callback_query_handler(cb_query_startswith('select_inbound:'), is_admin=True)
-async def select_inbounds(call: types.CallbackQuery):
+@bot.callback_query_handler(query_startswith('switch_service:'), is_admin=True)
+async def switch_service(call: types.CallbackQuery):
if not (username := mem_store.get(f'{call.message.chat.id}:username')):
return await bot.answer_callback_query(call.id, '❌ No user selected.', show_alert=True)
- protocols: dict[str, list[str]] = mem_store.get(f'{call.message.chat.id}:protocols', {})
- _, inbound, action = call.data.split(':')
- for protocol, inbounds in xray.config.inbounds_by_protocol.items():
- for i in inbounds:
- if i['tag'] != inbound:
- continue
- if not inbound in protocols[protocol]:
- protocols[protocol].append(inbound)
- else:
- protocols[protocol].remove(inbound)
- if len(protocols[protocol]) < 1:
- del protocols[protocol]
-
- mem_store.set(f'{call.message.chat.id}:protocols', protocols)
-
- if action in ["edit", "create_from_template"]:
- return await bot.edit_message_text(
- call.message.text,
- call.message.chat.id,
- call.message.message_id,
- reply_markup=BotKeyboard.select_protocols(
- protocols,
- "edit",
- username=username,
- data_limit=mem_store.get(f"{call.message.chat.id}:data_limit"),
- expire_date=mem_store.get(f"{call.message.chat.id}:expire_date"))
- )
- await bot.edit_message_text(
- call.message.text,
- call.message.chat.id,
- call.message.message_id,
- reply_markup=BotKeyboard.select_protocols(protocols, "create")
- )
-
+ selected_services: List[int] = mem_store.get(f'{call.message.chat.id}:services', [])
+ _, service, action = call.data.split(':')
+ service = int(service)
-@bot.callback_query_handler(cb_query_startswith('select_protocol:'), is_admin=True)
-async def select_protocols(call: types.CallbackQuery):
- if not (username := mem_store.get(f'{call.message.chat.id}:username')):
- return await bot.answer_callback_query(call.id, '❌ No user selected.', show_alert=True)
- protocols: dict[str, list[str]] = mem_store.get(f'{call.message.chat.id}:protocols', {})
- _, protocol, action = call.data.split(':')
- if protocol in protocols:
- del protocols[protocol]
+ with GetDB() as db:
+ services = crud.get_services(db)
+ if service in selected_services:
+ selected_services.remove(service)
else:
- protocols.update(
- {protocol: [inbound['tag'] for inbound in xray.config.inbounds_by_protocol[protocol]]})
- mem_store.set(f'{call.message.chat.id}:protocols', protocols)
+ selected_services.append(service)
+
+ mem_store.set(f'{call.message.chat.id}:services', selected_services)
- if action == ["edit", "create_from_template"]:
+ if action in ["edit"]:
return await bot.edit_message_text(
call.message.text,
call.message.chat.id,
call.message.message_id,
- reply_markup=BotKeyboard.select_protocols(
- protocols,
+ reply_markup=BotKeyboard.select_services(
+ services,
+ selected_services,
"edit",
username=username,
data_limit=mem_store.get(f"{call.message.chat.id}:data_limit"),
@@ -1275,11 +917,11 @@ async def select_protocols(call: types.CallbackQuery):
call.message.text,
call.message.chat.id,
call.message.message_id,
- reply_markup=BotKeyboard.select_protocols(protocols, action="create")
+ reply_markup=BotKeyboard.select_services(services, selected_services, "create")
)
-@bot.callback_query_handler(cb_query_startswith('confirm:'), is_admin=True)
+@bot.callback_query_handler(query_startswith('confirm:'), is_admin=True)
async def confirm_user_command(call: types.CallbackQuery):
data = call.data.split(':')[1]
chat_id = call.from_user.id
@@ -1292,21 +934,23 @@ async def confirm_user_command(call: types.CallbackQuery):
hour=23,
minute=59,
second=59)
- if data == 'delete':
- username = call.data.split(':')[2]
- with GetDB() as db:
- db_user = crud.get_user(db, username)
- crud.remove_user(db, db_user)
- await xray.operations.remove_user(db_user)
- await bot.edit_message_text(
- '✅ User deleted.',
- call.message.chat.id,
- call.message.message_id,
- reply_markup=BotKeyboard.main_menu()
- )
- if TELEGRAM_LOGGER_CHANNEL_ID:
- text = f'''\
+@bot.callback_query_handler(query_startswith("do_delete"), is_admin=True)
+async def delete_user(call: types.CallbackQuery):
+ username = call.data.split(':')[1]
+ with GetDB() as db:
+ db_user = crud.get_user(db, username)
+ crud.remove_user(db, db_user)
+ await xray.operations.remove_user(db_user)
+
+ await bot.edit_message_text(
+ '✅ User deleted.',
+ call.message.chat.id,
+ call.message.message_id,
+ reply_markup=BotKeyboard.main_menu()
+ )
+ if TELEGRAM_LOGGER_CHANNEL_ID:
+ text = f'''\
🗑 #Deleted #From_Bot
➖➖➖➖➖➖➖➖➖
Username : {db_user.username}
@@ -1314,305 +958,234 @@ async def confirm_user_command(call: types.CallbackQuery):
Expire Date : \
{datetime.fromtimestamp(db_user.expire).strftime('%H:%M:%S %Y-%m-%d') if db_user.expire else "Never"}
➖➖➖➖➖➖➖➖➖
-By : {full_name}'''
- try:
- await bot.send_message(TELEGRAM_LOGGER_CHANNEL_ID, text, 'HTML')
- except:
- pass
- elif data == "suspend":
- username = call.data.split(":")[2]
- with GetDB() as db:
- db_user = crud.get_user(db, username)
- crud.update_user(db, db_user, UserModify(
- status=UserStatusModify.disabled))
- await xray.operations.remove_user(db_user)
- user = UserResponse.model_validate(db_user)
- await bot.edit_message_text(
- get_user_info_text(
- status='disabled',
- username=username,
- sub_url=user.subscription_url,
- data_limit=db_user.data_limit,
- usage=db_user.used_traffic,
- expire=db_user.expire,
- note=user.note
- ),
- call.message.chat.id,
- call.message.message_id,
- parse_mode='HTML',
- reply_markup=BotKeyboard.user_menu(user_info={
- 'status': 'disabled',
- 'username': db_user.username
- }, note=True))
- if TELEGRAM_LOGGER_CHANNEL_ID:
- text = f'''\
+By : {call.from_user.full_name}'''
+ try:
+ await bot.send_message(TELEGRAM_LOGGER_CHANNEL_ID, text, 'HTML')
+ except:
+ pass
+
+
+@bot.callback_query_handler(query_startswith("do_suspend"), is_admin=True)
+async def disable_user(call: types.CallbackQuery):
+ username = call.data.split(":")[1]
+ with GetDB() as db:
+ db_user = crud.get_user(db, username)
+ crud.update_user(db, db_user, UserModify(
+ username=username,
+ status=UserStatusModify.disabled))
+ await xray.operations.remove_user(db_user)
+ user = UserResponse.model_validate(db_user)
+ await bot.edit_message_text(
+ get_user_info_text(
+ status='disabled',
+ username=username,
+ sub_url=user.subscription_url,
+ data_limit=db_user.data_limit,
+ usage=db_user.used_traffic,
+ expire=db_user.expire,
+ note=user.note
+ ),
+ call.message.chat.id,
+ call.message.message_id,
+ parse_mode='HTML',
+ reply_markup=BotKeyboard.user_menu(user_info={
+ 'status': 'disabled',
+ 'username': db_user.username
+ }, note=True))
+ if TELEGRAM_LOGGER_CHANNEL_ID:
+ text = f'''\
❌ #Disabled #From_Bot
➖➖➖➖➖➖➖➖➖
Username : {username}
➖➖➖➖➖➖➖➖➖
-By : {full_name}'''
- try:
- await bot.send_message(TELEGRAM_LOGGER_CHANNEL_ID, text, 'HTML')
- except:
- pass
- elif data == "activate":
- username = call.data.split(":")[2]
- with GetDB() as db:
- db_user = crud.get_user(db, username)
- crud.update_user(db, db_user, UserModify(
- status=UserStatusModify.active))
- await xray.operations.add_user(db_user)
- user = UserResponse.model_validate(db_user)
- await bot.edit_message_text(
- get_user_info_text(
- status='active',
- username=username,
- sub_url=user.subscription_url,
- data_limit=db_user.data_limit,
- usage=db_user.used_traffic,
- expire=db_user.expire,
- note=user.note
- ),
- call.message.chat.id,
- call.message.message_id,
- parse_mode='HTML',
- reply_markup=BotKeyboard.user_menu(user_info={
- 'status': 'active',
- 'username': db_user.username
- }, note=True))
- if TELEGRAM_LOGGER_CHANNEL_ID:
- text = f'''\
+By : {call.from_user.full_name}'''
+ try:
+ await bot.send_message(TELEGRAM_LOGGER_CHANNEL_ID, text, 'HTML')
+ except:
+ pass
+
+@bot.callback_query_handler(query_startswith("do_activate"), is_admin=True)
+async def activate_user(call: types.CallbackQuery):
+ username = call.data.split(":")[1]
+ with GetDB() as db:
+ db_user = crud.get_user(db, username)
+ crud.update_user(db, db_user, UserModify(
+ username=username,
+ status=UserStatusModify.active))
+ await xray.operations.add_user(db_user)
+ user = UserResponse.model_validate(db_user)
+ await bot.edit_message_text(
+ get_user_info_text(
+ status='active',
+ username=username,
+ sub_url=user.subscription_url,
+ data_limit=db_user.data_limit,
+ usage=db_user.used_traffic,
+ expire=db_user.expire,
+ note=user.note
+ ),
+ call.message.chat.id,
+ call.message.message_id,
+ parse_mode='HTML',
+ reply_markup=BotKeyboard.user_menu(user_info={
+ 'status': 'active',
+ 'username': db_user.username
+ }, note=True))
+ if TELEGRAM_LOGGER_CHANNEL_ID:
+ text = f'''\
✅ #Activated #From_Bot
➖➖➖➖➖➖➖➖➖
Username : {username}
➖➖➖➖➖➖➖➖➖
-By : {full_name}'''
- try:
- await bot.send_message(TELEGRAM_LOGGER_CHANNEL_ID, text, 'HTML')
- except:
- pass
- elif data == 'reset_usage':
- username = call.data.split(":")[2]
- with GetDB() as db:
- db_user = crud.get_user(db, username)
- crud.reset_user_data_usage(db, db_user)
- user = UserResponse.model_validate(db_user)
- await bot.edit_message_text(
- get_user_info_text(
- status=user.status,
- username=username,
- sub_url=user.subscription_url,
- data_limit=user.data_limit,
- usage=user.used_traffic,
- expire=user.expire,
- note=user.note
- ),
- call.message.chat.id,
- call.message.message_id,
- parse_mode='HTML',
- reply_markup=BotKeyboard.user_menu(user_info={
- 'status': user.status,
- 'username': user.username
- }, note=True))
- if TELEGRAM_LOGGER_CHANNEL_ID:
- text = f'''\
-🔁 #Reset_usage #From_Bot
-➖➖➖➖➖➖➖➖➖
+By : {call.from_user.full_name}'''
+ try:
+ await bot.send_message(TELEGRAM_LOGGER_CHANNEL_ID, text, 'HTML')
+ except:
+ pass
+
+@bot.callback_query_handler(query_startswith("do_reset_usage"), is_admin=True)
+async def reset_all_usages(call: types.CallbackQuery):
+ username = call.data.split(":")[1]
+ with GetDB() as db:
+ db_user = crud.get_user(db, username)
+ crud.reset_user_data_usage(db, db_user)
+ user = UserResponse.model_validate(db_user)
+ await bot.edit_message_text(
+ get_user_info_text(
+ status=user.status,
+ username=username,
+ sub_url=user.subscription_url,
+ data_limit=user.data_limit,
+ usage=user.used_traffic,
+ expire=user.expire,
+ note=user.note
+ ),
+ call.message.chat.id,
+ call.message.message_id,
+ parse_mode='HTML',
+ reply_markup=BotKeyboard.user_menu(user_info={
+ 'status': user.status,
+ 'username': user.username
+ }, note=True))
+ if TELEGRAM_LOGGER_CHANNEL_ID:
+ text = f'''\
+🔁 #Reset_usage #From_Bot
+➖➖➖➖➖➖➖➖➖
Username : {username}
➖➖➖➖➖➖➖➖➖
-By : {full_name}'''
- try:
- await bot.send_message(TELEGRAM_LOGGER_CHANNEL_ID, text, 'HTML')
- except:
- pass
- elif data == 'restart':
- m = await bot.edit_message_text(
- '🔄 Restarting XRay core...', call.message.chat.id, call.message.message_id)
- await xray.core.restart(xray.config.include_db_users())
- for node_id, node in list(xray.nodes.items()):
- if await node.is_healthy():
- await xray.operations.restart_node(node_id, xray.config.include_db_users())
- await bot.edit_message_text(
- '✅ XRay core restarted successfully.',
- m.chat.id, m.message_id,
+By : {call.from_user.full_name}'''
+ try:
+ await bot.send_message(TELEGRAM_LOGGER_CHANNEL_ID, text, 'HTML')
+ except:
+ pass
+
+
+@bot.callback_query_handler(query_equals("do_restart"), is_admin=True)
+async def restart_xray(call: types.CallbackQuery):
+ m = await bot.edit_message_text(
+ '🔄 Restarting XRay core...', call.message.chat.id, call.message.message_id)
+ await xray.core.restart(xray.config.include_db_users())
+ for node_id, node in list(xray.nodes.items()):
+ if await node.is_healthy():
+ await xray.operations.restart_node(node_id, xray.config.include_db_users())
+ await bot.edit_message_text(
+ '✅ XRay core restarted successfully.',
+ m.chat.id, m.message_id,
+ reply_markup=BotKeyboard.main_menu()
+ )
+
+@bot.callback_query_handler(query_equals("edit_user_done"), is_admin=True)
+async def edit_user(call: types.CallbackQuery):
+ if (username := mem_store.get(f'{call.message.chat.id}:username')) is None:
+ try:
+ await bot.delete_message(call.message.chat.id,
+ call.message.message_id)
+ except Exception:
+ pass
+ return await bot.send_message(
+ call.message.chat.id,
+ '❌ Bot reload detected. Please start over.',
reply_markup=BotKeyboard.main_menu()
)
- elif data in ['charge_add', 'charge_reset']:
- _, _, username, template_id = call.data.split(":")
- with GetDB() as db:
- template = crud.get_user_template(db, template_id)
- if not template:
- return await bot.answer_callback_query(call.id, "Template not found!", show_alert=True)
- template = UserTemplateResponse.model_validate(template)
-
- db_user = crud.get_user(db, username)
- if not db_user:
- return await bot.answer_callback_query(call.id, "User not found!", show_alert=True)
- user = UserResponse.model_validate(db_user)
+ if not mem_store.get(f'{call.message.chat.id}:services'):
+ return await bot.answer_callback_query(
+ call.id,
+ '❌ No Service selected.',
+ show_alert=True
+ )
- inbounds = template.inbounds
- proxies = {p.type.value: p.settings for p in db_user.proxies}
-
- for protocol in xray.config.inbounds_by_protocol:
- if protocol in inbounds and protocol not in db_user.inbounds:
- proxies.update({protocol: {}})
- elif protocol in db_user.inbounds and protocol not in inbounds:
- del proxies[protocol]
-
- crud.reset_user_data_usage(db, db_user)
- if data == 'charge_reset':
- expire_date = None
- if template.expire_duration:
- expire_date = today + relativedelta(seconds=template.expire_duration)
- modify = UserModify(
- status=UserStatus.active,
- expire=int(expire_date.timestamp()) if expire_date else 0,
- data_limit=template.data_limit,
- )
- else:
- expire_date = None
- if template.expire_duration:
- expire_date = (datetime.fromtimestamp(user.expire)
- if user.expire else today) + relativedelta(seconds=template.expire_duration)
- modify = UserModify(
- status=UserStatus.active,
- expire=int(expire_date.timestamp()) if expire_date else 0,
- data_limit=(user.data_limit or 0) - user.used_traffic + template.data_limit,
- )
- db_user = crud.update_user(db, db_user, modify)
- await xray.operations.add_user(db_user)
-
- text = get_user_info_text(
- status=db_user.status,
- username=username,
- sub_url=user.subscription_url,
- expire=db_user.expire,
- data_limit=db_user.data_limit,
- usage=db_user.used_traffic,
- note=db_user.note)
-
- await bot.edit_message_text(
- f'🔋 User Successfully Charged!\n\n{text}',
- call.message.chat.id,
- call.message.message_id,
- parse_mode='html',
- reply_markup=BotKeyboard.user_menu(user_info={
- 'status': user.status,
- 'username': user.username
- }, note=True))
- if TELEGRAM_LOGGER_CHANNEL_ID:
- text = f'''\
-🔋 #Charged #{data.split('_')[1].title()} #From_Bot
-➖➖➖➖➖➖➖➖➖
-Template : {template.name}
-Username : {user.username}
-➖➖➖➖➖➖➖➖➖
-Last status
-├Traffic Limit : {readable_size(user.data_limit) if user.data_limit else "Unlimited"}
-├Expire Date : \
-{datetime.fromtimestamp(user.expire).strftime('%H:%M:%S %Y-%m-%d') if user.expire else "Never"}
-➖➖➖➖➖➖➖➖➖
-New status
-├Traffic Limit : {readable_size(db_user.data_limit) if db_user.data_limit else "Unlimited"}
-├Expire Date : \
-{datetime.fromtimestamp(db_user.expire).strftime('%H:%M:%S %Y-%m-%d') if db_user.expire else "Never"}
-➖➖➖➖➖➖➖➖➖
-By : {full_name}\
-'''
- try:
- await bot.send_message(TELEGRAM_LOGGER_CHANNEL_ID, text, 'HTML')
- except:
- pass
+ #inbounds: dict[str, list[str]] = {
+ # k: v for k, v in mem_store.get(f'{call.message.chat.id}:protocols').items() if v}
+ services: List[int] = mem_store.get(f"{call.message.chat.id}:services")
- elif data == 'edit_user':
- if (username := mem_store.get(f'{call.message.chat.id}:username')) is None:
- try:
- await bot.delete_message(call.message.chat.id,
- call.message.message_id)
- except Exception:
- pass
- return await bot.send_message(
- call.message.chat.id,
- '❌ Bot reload detected. Please start over.',
- reply_markup=BotKeyboard.main_menu()
- )
+ with GetDB() as db:
+ db_user = crud.get_user(db, username)
+ if not db_user:
+ return await bot.answer_callback_query(call.id, text=f"User not found!", show_alert=True)
- if not mem_store.get(f'{call.message.chat.id}:protocols'):
- return await bot.answer_callback_query(
- call.id,
- '❌ No inbounds selected.',
- show_alert=True
- )
+ # proxies = {p.type.value: p.settings for p in db_user.proxies}
- inbounds: dict[str, list[str]] = {
- k: v for k, v in mem_store.get(f'{call.message.chat.id}:protocols').items() if v}
+ """for protocol in xray.config.inbounds_by_protocol:
+ if protocol in inbounds and protocol not in db_user.inbounds:
+ proxies.update({protocol: {'flow': TELEGRAM_DEFAULT_VLESS_FLOW} if
+ TELEGRAM_DEFAULT_VLESS_FLOW and protocol == ProxyTypes.VLESS else {}})
+ elif protocol in db_user.inbounds and protocol not in inbounds:
+ del proxies[protocol]"""
- with GetDB() as db:
- db_user = crud.get_user(db, username)
- if not db_user:
- return await bot.answer_callback_query(call.id, text=f"User not found!", show_alert=True)
-
- proxies = {p.type.value: p.settings for p in db_user.proxies}
-
- for protocol in xray.config.inbounds_by_protocol:
- if protocol in inbounds and protocol not in db_user.inbounds:
- proxies.update({protocol: {'flow': TELEGRAM_DEFAULT_VLESS_FLOW} if
- TELEGRAM_DEFAULT_VLESS_FLOW and protocol == ProxyTypes.VLESS else {}})
- elif protocol in db_user.inbounds and protocol not in inbounds:
- del proxies[protocol]
-
- modify = UserModify(
- expire=int(mem_store.get(f'{call.message.chat.id}:expire_date').timestamp())
- if mem_store.get(f'{call.message.chat.id}:expire_date') else 0, data_limit=mem_store.get(
- f"{call.message.chat.id}:data_limit"),
- proxies=proxies, inbounds=inbounds)
- last_user = UserResponse.model_validate(db_user)
- db_user = crud.update_user(db, db_user, modify)
+ modify = UserModify(
+ username=username,
+ # status=db_user.status,
+ expire=int(mem_store.get(f'{call.message.chat.id}:expire_date').timestamp())
+ if mem_store.get(f'{call.message.chat.id}:expire_date') else 0,
+ data_limit=mem_store.get(f"{call.message.chat.id}:data_limit"),
+ services=services)
+ last_user = UserResponse.model_validate(db_user)
+ db_user = crud.update_user(db, db_user, modify)
- user = UserResponse.model_validate(db_user)
+ user = UserResponse.model_validate(db_user)
- if user.status == UserStatus.active:
- await xray.operations.update_user(db_user)
- else:
- await xray.operations.remove_user(db_user)
+ if user.status == UserStatus.active:
+ await xray.operations.update_user(db_user)
+ else:
+ await xray.operations.remove_user(db_user)
- await bot.answer_callback_query(call.id, "✅ User updated successfully.")
+ await bot.answer_callback_query(call.id, "✅ User updated successfully.")
- text = get_user_info_text(
- status=user.status,
- username=user.username,
- sub_url=user.subscription_url,
- data_limit=user.data_limit,
- usage=user.used_traffic,
- expire=user.expire,
- note=user.note
- )
- await bot.edit_message_text(
- text,
- call.message.chat.id,
- call.message.message_id,
- parse_mode="HTML",
- reply_markup=BotKeyboard.user_menu({
- 'username': db_user.username,
- 'status': db_user.status},
- note=True)
- )
- if TELEGRAM_LOGGER_CHANNEL_ID:
- tag = f'\n➖➖➖➖➖➖➖➖➖ \nBy : {full_name}'
- if last_user.data_limit != user.data_limit:
- text = f'''\
+ text = get_user_info_text(
+ status=user.status,
+ username=user.username,
+ sub_url=user.subscription_url,
+ data_limit=user.data_limit,
+ usage=user.used_traffic,
+ expire=user.expire,
+ note=user.note
+ )
+ await bot.edit_message_text(
+ text,
+ call.message.chat.id,
+ call.message.message_id,
+ parse_mode="HTML",
+ reply_markup=BotKeyboard.user_menu({
+ 'username': db_user.username,
+ 'status': db_user.status},
+ note=True)
+ )
+ if TELEGRAM_LOGGER_CHANNEL_ID:
+ tag = f'\n➖➖➖➖➖➖➖➖➖ \nBy : {call.from_user.full_name}'
+ if last_user.data_limit != user.data_limit:
+ text = f'''\
📶 #Traffic_Change #From_Bot
➖➖➖➖➖➖➖➖➖
Username : {user.username}
Last Traffic Limit : {readable_size(last_user.data_limit) if last_user.data_limit else "Unlimited"}
New Traffic Limit : {readable_size(user.data_limit) if user.data_limit else "Unlimited"}{tag}'''
- try:
- await bot.send_message(TELEGRAM_LOGGER_CHANNEL_ID, text, 'HTML')
- except:
- pass
- if last_user.expire != user.expire:
- text = f'''\
+ try:
+ await bot.send_message(TELEGRAM_LOGGER_CHANNEL_ID, text, 'HTML')
+ except:
+ pass
+ if last_user.expire != user.expire:
+ text = f'''\
📅 #Expiry_Change #From_Bot
➖➖➖➖➖➖➖➖➖
Username : {user.username}
@@ -1620,343 +1193,278 @@ async def confirm_user_command(call: types.CallbackQuery):
{datetime.fromtimestamp(last_user.expire).strftime('%H:%M:%S %Y-%m-%d') if last_user.expire else "Never"}
New Expire Date : \
{datetime.fromtimestamp(user.expire).strftime('%H:%M:%S %Y-%m-%d') if user.expire else "Never"}{tag}'''
- try:
- await bot.send_message(TELEGRAM_LOGGER_CHANNEL_ID, text, 'HTML')
- except:
- pass
- if list(last_user.inbounds.values())[0] != list(user.inbounds.values())[0]:
- text = f'''\
+ try:
+ await bot.send_message(TELEGRAM_LOGGER_CHANNEL_ID, text, 'HTML')
+ except:
+ pass
+ if list(last_user.inbounds.values())[0] != list(user.inbounds.values())[0]:
+ text = f'''\
⚙️ #Inbounds_Change #From_Bot
➖➖➖➖➖➖➖➖➖
Username : {user.username}
Last Proxies : {", ".join(list(last_user.inbounds.values())[0])}
New Proxies : {", ".join(list(user.inbounds.values())[0])}{tag}'''
- try:
- await bot.send_message(TELEGRAM_LOGGER_CHANNEL_ID, text, 'HTML')
- except:
- pass
-
- elif data == 'add_user':
- if mem_store.get(f'{call.message.chat.id}:username') is None:
try:
- await bot.delete_message(call.message.chat.id,
- call.message.message_id)
- except Exception:
+ await bot.send_message(TELEGRAM_LOGGER_CHANNEL_ID, text, 'HTML')
+ except:
pass
- return await bot.send_message(
- call.message.chat.id,
- '❌ Bot reload detected. Please start over.',
- reply_markup=BotKeyboard.main_menu()
- )
- if not mem_store.get(f'{call.message.chat.id}:protocols'):
- return await bot.answer_callback_query(
- call.id,
- '❌ No inbounds selected.',
- show_alert=True
- )
- inbounds: dict[str, list[str]] = {
- k: v for k, v in mem_store.get(f'{call.message.chat.id}:protocols').items() if v}
- proxies = {p: ({'flow': TELEGRAM_DEFAULT_VLESS_FLOW} if
- TELEGRAM_DEFAULT_VLESS_FLOW and p == ProxyTypes.VLESS else {}) for p in inbounds}
-
- new_user = UserCreate(
- username=mem_store.get(f'{call.message.chat.id}:username'),
- expire=int(mem_store.get(f'{call.message.chat.id}:expire_date').timestamp())
- if mem_store.get(f'{call.message.chat.id}:expire_date') else None,
- data_limit=mem_store.get(f'{call.message.chat.id}:data_limit')
- if mem_store.get(f'{call.message.chat.id}:data_limit') else None,
- proxies=proxies,
- inbounds=inbounds)
-
- for proxy_type in new_user.proxies:
- if not xray.config.inbounds_by_protocol.get(proxy_type):
- return await bot.answer_callback_query(
- call.id,
- f'❌ Protocol {proxy_type} is disabled on your server',
- show_alert=True
- )
+@bot.callback_query_handler(query_startswith("add_user_done"), is_admin=True)
+async def delete_limpired(call: types.CallbackQuery):
+ if mem_store.get(f'{call.message.chat.id}:username') is None:
try:
- with GetDB() as db:
- db_user = crud.create_user(db, new_user)
- proxies = db_user.proxies
- user = UserResponse.model_validate(db_user)
- except sqlalchemy.exc.IntegrityError:
- db.rollback()
- return await bot.answer_callback_query(
- call.id,
- '❌ Username already exists.',
- show_alert=True
- )
+ await bot.delete_message(call.message.chat.id,
+ call.message.message_id)
+ except Exception:
+ pass
+ return await bot.send_message(
+ call.message.chat.id,
+ '❌ Bot reload detected. Please start over.',
+ reply_markup=BotKeyboard.main_menu()
+ )
- await xray.operations.add_user(db_user)
+ if not mem_store.get(f'{call.message.chat.id}:services'):
+ return await bot.answer_callback_query(
+ call.id,
+ '❌ No services selected.',
+ show_alert=True
+ )
- text = get_user_info_text(
- status=user.status,
- username=user.username,
- sub_url=user.subscription_url,
- data_limit=user.data_limit,
- usage=user.used_traffic,
- expire=user.expire,
- note=user.note
+ services: List[int] = mem_store.get(f'{call.message.chat.id}:services', [])
+
+ new_user = UserCreate(
+ username=mem_store.get(f'{call.message.chat.id}:username'),
+ expire=int(mem_store.get(f'{call.message.chat.id}:expire_date').timestamp())
+ if mem_store.get(f'{call.message.chat.id}:expire_date') else None,
+ data_limit=mem_store.get(f'{call.message.chat.id}:data_limit')
+ if mem_store.get(f'{call.message.chat.id}:data_limit') else None,
+ services=services)
+ try:
+ with GetDB() as db:
+ db_user = crud.create_user(db, new_user)
+ services = db_user.services
+ user = UserResponse.model_validate(db_user)
+ except sqlalchemy.exc.IntegrityError:
+ db.rollback()
+ return await bot.answer_callback_query(
+ call.id,
+ '❌ Username already exists.',
+ show_alert=True
)
- await bot.edit_message_text(
- text,
- call.message.chat.id,
- call.message.message_id,
- parse_mode="HTML",
- reply_markup=BotKeyboard.user_menu(user_info={'status': user.status, 'username': user.username}, note=True))
- if TELEGRAM_LOGGER_CHANNEL_ID:
- text = f'''\
+ await xray.operations.add_user(db_user)
+
+ text = get_user_info_text(
+ status=user.status,
+ username=user.username,
+ sub_url=user.subscription_url,
+ data_limit=user.data_limit,
+ usage=user.used_traffic,
+ expire=user.expire,
+ note=user.note
+ )
+ await bot.edit_message_text(
+ text,
+ call.message.chat.id,
+ call.message.message_id,
+ parse_mode="HTML",
+ reply_markup=BotKeyboard.user_menu(user_info={'status': user.status, 'username': user.username}, note=True))
+
+ if TELEGRAM_LOGGER_CHANNEL_ID:
+ text = f'''\
🆕 #Created #From_Bot
➖➖➖➖➖➖➖➖➖
Username : {user.username}
Traffic Limit : {readable_size(user.data_limit) if user.data_limit else "Unlimited"}
Expire Date : \
{datetime.fromtimestamp(user.expire).strftime('%H:%M:%S %Y-%m-%d') if user.expire else "Never"}
-Proxies : {"" if not proxies else ", ".join([proxy.type for proxy in proxies])}
+Services : {"" if not services else ", ".join([s.name for s in services])}
➖➖➖➖➖➖➖➖➖
-By : {full_name}'''
- try:
- await bot.send_message(TELEGRAM_LOGGER_CHANNEL_ID, text, 'HTML')
- except:
- pass
+By : {call.from_user.full_name}'''
+ try:
+ await bot.send_message(TELEGRAM_LOGGER_CHANNEL_ID, text, 'HTML')
+ except:
+ pass
- elif data in ['delete_expired', 'delete_limited']:
- await bot.edit_message_text(
- '⏳ In Progress...',
- call.message.chat.id,
- call.message.message_id,
- parse_mode="HTML")
- with GetDB() as db:
- depleted_users = crud.get_users(
- db, status=[UserStatus.limited if data == 'delete_limited' else UserStatus.expired])
- file_name = f'{data[8:]}_users_{int(now.timestamp()*1000)}.txt'
- with open(file_name, 'w') as f:
- f.write('USERNAME\tEXIPRY\tUSAGE/LIMIT\tSTATUS\n')
- deleted = 0
- for user in depleted_users:
- try:
- crud.remove_user(db, user)
- await xray.operations.remove_user(user)
- deleted +=1
- f.write(\
+
+@bot.callback_query_handler(query_startswith(['do_delete_expired', 'do_delete_limited']), is_admin=True)
+async def delete_limpired(call: types.CallbackQuery):
+ await bot.edit_message_text(
+ '⏳ In Progress...',
+ call.message.chat.id,
+ call.message.message_id,
+ parse_mode="HTML")
+ with GetDB() as db:
+ depleted_users = crud.get_users(
+ db, status=[UserStatus.limited if data == 'delete_limited' else UserStatus.expired])
+ file_name = f'{data[8:]}_users_{int(now.timestamp()*1000)}.txt'
+ with open(file_name, 'w') as f:
+ f.write('USERNAME\tEXIPRY\tUSAGE/LIMIT\tSTATUS\n')
+ deleted = 0
+ for user in depleted_users:
+ try:
+ crud.remove_user(db, user)
+ await xray.operations.remove_user(user)
+ deleted +=1
+ f.write(\
f'{user.username}\
\t{datetime.fromtimestamp(user.expire) if user.expire else "never"}\
\t{readable_size(user.used_traffic) if user.used_traffic else 0}\
/{readable_size(user.data_limit) if user.data_limit else "Unlimited"}\
\t{user.status}\n')
- except:
- db.rollback()
- await bot.edit_message_text(
- f'✅ {deleted}/{len(depleted_users)} {data[7:].title()} Users Deleted',
- call.message.chat.id,
- call.message.message_id,
- parse_mode="HTML",
- reply_markup=BotKeyboard.main_menu())
- if TELEGRAM_LOGGER_CHANNEL_ID:
- text = f'''\
+ except:
+ db.rollback()
+ await bot.edit_message_text(
+ f'✅ {deleted}/{len(depleted_users)} {data[7:].title()} Users Deleted',
+ call.message.chat.id,
+ call.message.message_id,
+ parse_mode="HTML",
+ reply_markup=BotKeyboard.main_menu())
+ if TELEGRAM_LOGGER_CHANNEL_ID:
+ text = f'''\
🗑 #Delete #{data[7:].title()} #From_Bot
➖➖➖➖➖➖➖➖➖
Count: {deleted}
➖➖➖➖➖➖➖➖➖
-By : {full_name}'''
+By : {call.from_user.full_name}'''
+ try:
+ await bot.send_document(TELEGRAM_LOGGER_CHANNEL_ID, open(file_name, 'rb'), caption=text, parse_mode='HTML')
+ os.remove(file_name)
+ except:
+ pass
+
+@bot.callback_query_handler(query_startswith('do_add_data:'), is_admin=True)
+async def add_data_to_all(call: types.CallbackQuery):
+ schedule_delete_message(
+ call.message.chat.id,
+ (await bot.send_message(chat_id, '⏳ In Progress...', 'HTML')).id)
+ data_limit = float(call.data.split(":")[1]) * 1024 * 1024 * 1024
+ with GetDB() as db:
+ users = crud.get_users(db)
+ counter = 0
+ file_name = f'new_data_limit_users_{int(now.timestamp()*1000)}.txt'
+ with open(file_name, 'w') as f:
+ f.write('USERNAME\tEXIPRY\tUSAGE/LIMIT\tSTATUS\n')
+ for user in users:
try:
- await bot.send_document(TELEGRAM_LOGGER_CHANNEL_ID, open(file_name, 'rb'), caption=text, parse_mode='HTML')
- os.remove(file_name)
- except:
- pass
- elif data == 'add_data':
- schedule_delete_message(
- call.message.chat.id,
- (await bot.send_message(chat_id, '⏳ In Progress...', 'HTML')).id)
- data_limit = float(call.data.split(":")[2]) * 1024 * 1024 * 1024
- with GetDB() as db:
- users = crud.get_users(db)
- counter = 0
- file_name = f'new_data_limit_users_{int(now.timestamp()*1000)}.txt'
- with open(file_name, 'w') as f:
- f.write('USERNAME\tEXIPRY\tUSAGE/LIMIT\tSTATUS\n')
- for user in users:
- try:
- if user.data_limit and user.status not in [UserStatus.limited, UserStatus.expired]:
- user = crud.update_user(db, user, UserModify(data_limit=(user.data_limit + data_limit)))
- counter += 1
- f.write(
- f'{user.username}\
+ if user.data_limit and user.status not in [UserStatus.limited, UserStatus.expired]:
+ user = crud.update_user(db, user, UserModify(username=user.username, data_limit=(user.data_limit + data_limit)))
+ counter += 1
+ f.write(
+ f'{user.username}\
\t{datetime.fromtimestamp(user.expire) if user.expire else "never"}\
\t{readable_size(user.used_traffic) if user.used_traffic else 0}\
/{readable_size(user.data_limit) if user.data_limit else "Unlimited"}\
\t{user.status}\n')
- except:
- db.rollback()
- await cleanup_messages(chat_id)
- await bot.send_message(
- chat_id,
- f'✅ {counter}/{len(users)} Users Data Limit according to {"+" if data_limit > 0 else "-"}{readable_size(abs(data_limit))}',
- 'HTML',
- reply_markup=BotKeyboard.main_menu())
- if TELEGRAM_LOGGER_CHANNEL_ID:
- text = f'''\
+ except:
+ db.rollback()
+ await cleanup_messages(chat_id)
+ await bot.send_message(
+ chat_id,
+ f'✅ {counter}/{len(users)} Users Data Limit according to {"+" if data_limit > 0 else "-"}{readable_size(abs(data_limit))}',
+ 'HTML',
+ reply_markup=BotKeyboard.main_menu())
+ if TELEGRAM_LOGGER_CHANNEL_ID:
+ text = f'''\
📶 #Traffic_Change #From_Bot
➖➖➖➖➖➖➖➖➖
According to: {"+" if data_limit > 0 else "-"}{readable_size(abs(data_limit))}
Count: {counter}
➖➖➖➖➖➖➖➖➖
-By : {full_name}'''
- try:
- await bot.send_document(TELEGRAM_LOGGER_CHANNEL_ID, open(file_name, 'rb'), caption=text, parse_mode='HTML')
- os.remove(file_name)
- except:
- pass
+By : {call.from_user.full_name}'''
+ try:
+ await bot.send_document(TELEGRAM_LOGGER_CHANNEL_ID, open(file_name, 'rb'), caption=text, parse_mode='HTML')
+ os.remove(file_name)
+ except:
+ pass
- elif data == 'add_time':
- schedule_delete_message(
- call.message.chat.id,
- (await bot.send_message(chat_id, '⏳ In Progress...', 'HTML')).id)
- days = int(call.data.split(":")[2])
- with GetDB() as db:
- users = crud.get_users(db)
- counter = 0
- file_name = f'new_expiry_users_{int(now.timestamp()*1000)}.txt'
- with open(file_name, 'w') as f:
- f.write('USERNAME\tEXIPRY\tUSAGE/LIMIT\tSTATUS\n')
- for user in users:
- try:
- if user.expire and user.status not in [UserStatus.limited, UserStatus.expired]:
- user = crud.update_user(
- db, user,
- UserModify(
- expire=int(
- (datetime.fromtimestamp(user.expire) + relativedelta(days=days)).timestamp())))
- counter += 1
- f.write(
- f'{user.username}\
+@bot.callback_query_handler(query_startswith('do_add_time:'), is_admin=True)
+async def add_time_to_all(call: types.CallbackQuery):
+ schedule_delete_message(
+ call.message.chat.id,
+ (await bot.send_message(chat_id, '⏳ In Progress...', 'HTML')).id)
+ days = int(call.data.split(":")[1])
+ with GetDB() as db:
+ users = crud.get_users(db)
+ counter = 0
+ file_name = f'new_expiry_users_{int(now.timestamp()*1000)}.txt'
+ with open(file_name, 'w') as f:
+ f.write('USERNAME\tEXIPRY\tUSAGE/LIMIT\tSTATUS\n')
+ for user in users:
+ try:
+ if user.expire and user.status not in [UserStatus.limited, UserStatus.expired]:
+ user = crud.update_user(
+ db, user,
+ UserModify(
+ username=user.username,
+ expire=int(
+ (datetime.fromtimestamp(user.expire) + relativedelta(days=days)).timestamp())))
+ counter += 1
+ f.write(
+ f'{user.username}\
\t{datetime.fromtimestamp(user.expire) if user.expire else "never"}\
\t{readable_size(user.used_traffic) if user.used_traffic else 0}\
/{readable_size(user.data_limit) if user.data_limit else "Unlimited"}\
\t{user.status}\n')
- except:
- db.rollback()
- await cleanup_messages(chat_id)
- await bot.send_message(
- chat_id,
- f'✅ {counter}/{len(users)} Users Expiry Changes according to {days} Days',
- 'HTML',
- reply_markup=BotKeyboard.main_menu())
- if TELEGRAM_LOGGER_CHANNEL_ID:
- text = f'''\
+ except:
+ db.rollback()
+ await cleanup_messages(chat_id)
+ await bot.send_message(
+ chat_id,
+ f'✅ {counter}/{len(users)} Users Expiry Changes according to {days} Days',
+ 'HTML',
+ reply_markup=BotKeyboard.main_menu())
+ if TELEGRAM_LOGGER_CHANNEL_ID:
+ text = f'''\
📅 #Expiry_Change #From_Bot
➖➖➖➖➖➖➖➖➖
According to: {days} Days
Count: {counter}
➖➖➖➖➖➖➖➖➖
-By : {full_name}'''
- try:
- await bot.send_document(TELEGRAM_LOGGER_CHANNEL_ID, open(file_name, 'rb'), caption=text, parse_mode='HTML')
- os.remove(file_name)
- except:
- pass
- elif data in ['inbound_add', 'inbound_remove']:
- await bot.edit_message_text(
- '⏳ In Progress...',
+By : {call.from_user.full_name}'''
+ try:
+ await bot.send_document(TELEGRAM_LOGGER_CHANNEL_ID, open(file_name, 'rb'), caption=text, parse_mode='HTML')
+ os.remove(file_name)
+ except:
+ pass
+
+@bot.callback_query_handler(query_startswith('do_revoke:'), is_admin=True)
+async def revoke_sub(call: types.CallbackQuery):
+ username = call.data.split(":")[1]
+ with GetDB() as db:
+ db_user = crud.get_user(db, username)
+ if not db_user:
+ return await bot.answer_callback_query(call.id, text=f"User not found!", show_alert=True)
+ db_user = crud.revoke_user_sub(db, db_user)
+ user = UserResponse.model_validate(db_user)
+ text = get_user_info_text(
+ status=user.status,
+ username=user.username,
+ sub_url=user.subscription_url,
+ expire=user.expire,
+ data_limit=user.data_limit,
+ usage=user.used_traffic,
+ note=user.note)
+ await bot.edit_message_text(
+ f'✅ Subscription Successfully Revoked!\n\n{text}',
call.message.chat.id,
call.message.message_id,
- parse_mode="HTML")
- inbound = call.data.split(":")[2]
- with GetDB() as db:
- users = crud.get_users(db)
- unsuccessful = 0
- for user in users:
- inbound_tags = [j for i in user.inbounds for j in user.inbounds[i]]
- protocol = xray.config.inbounds_by_tag[inbound]['protocol']
- new_inbounds = user.inbounds
- if data == 'inbound_add':
- if inbound not in inbound_tags:
- if protocol in list(new_inbounds.keys()):
- new_inbounds[protocol].append(inbound)
- else:
- new_inbounds[protocol] = [inbound]
- elif data == 'inbound_remove':
- if inbound in inbound_tags:
- if len(new_inbounds[protocol]) == 1:
- del new_inbounds[protocol]
- else:
- new_inbounds[protocol].remove(inbound)
- if (data == 'inbound_remove' and inbound in inbound_tags)\
- or (data == 'inbound_add' and inbound not in inbound_tags):
- proxies = {p.type.value: p.settings for p in user.proxies}
- for protocol in xray.config.inbounds_by_protocol:
- if protocol in new_inbounds and protocol not in user.inbounds:
- proxies.update({protocol: {'flow': TELEGRAM_DEFAULT_VLESS_FLOW} if
- TELEGRAM_DEFAULT_VLESS_FLOW and protocol == ProxyTypes.VLESS else {}})
- elif protocol in user.inbounds and protocol not in new_inbounds:
- del proxies[protocol]
- try:
- user = crud.update_user(db, user, UserModify(inbounds=new_inbounds, proxies=proxies))
- if user.status == UserStatus.active:
- await xray.operations.update_user(user)
- except:
- db.rollback()
- unsuccessful += 1
-
- await bot.edit_message_text(
- f'✅ {data[8:].title()} {inbound} Users Successfully'+\
- (f'\n Unsuccessful: {unsuccessful}' if unsuccessful else ''),
- call.message.chat.id,
- call.message.message_id,
- parse_mode="HTML",
- reply_markup=BotKeyboard.main_menu())
-
- if TELEGRAM_LOGGER_CHANNEL_ID:
- text = f'''\
-✏️ #Modified #Inbound_{data[8:].title()} #From_Bot
-➖➖➖➖➖➖➖➖➖
-Inbound: {inbound}
-➖➖➖➖➖➖➖➖➖
-By : {full_name}'''
- try:
- await bot.send_message(TELEGRAM_LOGGER_CHANNEL_ID, text, 'HTML')
- except:
- pass
-
- elif data == 'revoke_sub':
- username = call.data.split(":")[2]
- with GetDB() as db:
- db_user = crud.get_user(db, username)
- if not db_user:
- return await bot.answer_callback_query(call.id, text=f"User not found!", show_alert=True)
- db_user = crud.revoke_user_sub(db, db_user)
- user = UserResponse.model_validate(db_user)
- text = get_user_info_text(
- status=user.status,
- username=user.username,
- sub_url=user.subscription_url,
- expire=user.expire,
- data_limit=user.data_limit,
- usage=user.used_traffic,
- note=user.note)
- await bot.edit_message_text(
- f'✅ Subscription Successfully Revoked!\n\n{text}',
- call.message.chat.id,
- call.message.message_id,
- parse_mode="HTML",
- reply_markup=BotKeyboard.user_menu(user_info={'status': user.status, 'username': user.username}, note=True))
+ parse_mode="HTML",
+ reply_markup=BotKeyboard.user_menu(user_info={'status': user.status, 'username': user.username}, note=True))
- if TELEGRAM_LOGGER_CHANNEL_ID:
- text = f'''\
+ if TELEGRAM_LOGGER_CHANNEL_ID:
+ text = f'''\
🚫 #Revoke_sub #From_Bot
➖➖➖➖➖➖➖➖➖
Username: {username}
➖➖➖➖➖➖➖➖➖
-By : {full_name}'''
- try:
- await bot.send_message(TELEGRAM_LOGGER_CHANNEL_ID, text, 'HTML')
- except:
- pass
+By : {call.from_user.full_name}'''
+ try:
+ await bot.send_message(TELEGRAM_LOGGER_CHANNEL_ID, text, 'HTML')
+ except:
+ pass
@bot.message_handler(func=lambda message: True, is_admin=True)
diff --git a/app/telegram/utils/custom_filters.py b/app/telegram/utils/custom_filters.py
index d69cdec..e5db305 100644
--- a/app/telegram/utils/custom_filters.py
+++ b/app/telegram/utils/custom_filters.py
@@ -1,6 +1,7 @@
from app.telegram import bot
+from typing import Union
-from telebot import types
+from telebot import types, custom_filters
from telebot.asyncio_filters import AdvancedCustomFilter, StateFilter
from config import TELEGRAM_ADMIN_ID
@@ -18,12 +19,15 @@ class IsAdminFilter(AdvancedCustomFilter):
return message.chat.id in TELEGRAM_ADMIN_ID
-def cb_query_equals(text: str):
+def query_equals(text: str):
return lambda query: query.data == text
-def cb_query_startswith(text: str):
- return lambda query: query.data.startswith(text)
+def query_startswith(text: Union[list, str]):
+ if type(text) is str:
+ return lambda query: query.data.startswith(text)
+ else:
+ return lambda query: query.data.startswith(tuple(text))
diff --git a/app/telegram/utils/keyboard.py b/app/telegram/utils/keyboard.py
index 9dc815c..19f735c 100644
--- a/app/telegram/utils/keyboard.py
+++ b/app/telegram/utils/keyboard.py
@@ -25,8 +25,8 @@ class BotKeyboard:
keyboard.add(
types.InlineKeyboardButton(text='👥 Users', callback_data='users:1'),
types.InlineKeyboardButton(text='✏️ Edit All Users', callback_data='edit_all'))
- keyboard.add(
- types.InlineKeyboardButton(text='➕ Create User From Template', callback_data='template_add_user'))
+ #keyboard.add(
+ # types.InlineKeyboardButton(text='➕ Create User From Template', callback_data='template_add_user'))
keyboard.add(
types.InlineKeyboardButton(text='➕ Create User', callback_data='add_user'))
return keyboard
@@ -41,40 +41,20 @@ class BotKeyboard:
keyboard.add(
types.InlineKeyboardButton(text='🔋 Data (➕|➖)', callback_data='add_data'),
types.InlineKeyboardButton(text='📅 Time (➕|➖)', callback_data='add_time'))
- keyboard.add(
- types.InlineKeyboardButton(text='➕ Add Inbound', callback_data='inbound_add'),
- types.InlineKeyboardButton(text='➖ Remove Inbound', callback_data='inbound_remove'))
- keyboard.add(types.InlineKeyboardButton(text='🔙 Back', callback_data='cancel'))
- return keyboard
-
-
- @staticmethod
- def inbounds_menu(action, inbounds):
- keyboard = types.InlineKeyboardMarkup()
- for inbound in inbounds:
- keyboard.add(types.InlineKeyboardButton(text=inbound, callback_data=f'confirm_{action}:{inbound}'))
+ #keyboard.add(
+ # types.InlineKeyboardButton(text='➕ Add Inbound', callback_data='inbound_add'),
+ # types.InlineKeyboardButton(text='➖ Remove Inbound', callback_data='inbound_remove'))
keyboard.add(types.InlineKeyboardButton(text='🔙 Back', callback_data='cancel'))
return keyboard
- @staticmethod
- def templates_menu(templates: Dict[str, int], username: str = None):
- keyboard = types.InlineKeyboardMarkup()
-
- for chunk in chunk_dict(templates):
- row = []
- for name, _id in chunk.items():
- row.append(
- types.InlineKeyboardButton(
- text=name,
- callback_data=f'template_charge:{_id}:{username}' if username else f"template_add_user:{_id}"))
- keyboard.add(*row)
-
- keyboard.add(
- types.InlineKeyboardButton(
- text='🔙 Back',
- callback_data=f'user:{username}' if username else 'cancel'))
- return keyboard
+ #@staticmethod
+ #def inbounds_menu(action, inbounds):
+ # keyboard = types.InlineKeyboardMarkup()
+ # for inbound in inbounds:
+ # keyboard.add(types.InlineKeyboardButton(text=inbound, callback_data=f'confirm_{action}:{inbound}'))
+ # keyboard.add(types.InlineKeyboardButton(text='🔙 Back', callback_data='cancel'))
+ # return keyboard
@staticmethod
@@ -180,7 +160,7 @@ class BotKeyboard:
keyboard.add(
types.InlineKeyboardButton(
text='Yes',
- callback_data=f"confirm:{action}:{username}"
+ callback_data=f"{action}:{username}"
),
types.InlineKeyboardButton(
text='No',
@@ -189,27 +169,6 @@ class BotKeyboard:
)
return keyboard
- @staticmethod
- def charge_add_or_reset(username: str, template_id: int):
- keyboard = types.InlineKeyboardMarkup()
- keyboard.add(
- types.InlineKeyboardButton(
- text='🔰 Add to current',
- callback_data=f"confirm:charge_add:{username}:{template_id}"
- ),
- types.InlineKeyboardButton(
- text='♻️ Reset',
- callback_data=f"confirm:charge_reset:{username}:{template_id}"
- ))
- keyboard.add(
- types.InlineKeyboardButton(
- text="Cancel",
- callback_data=f'user:{username}'
- )
- )
- return keyboard
-
-
@staticmethod
def inline_cancel_action(callback_data: str = "cancel"):
keyboard = types.InlineKeyboardMarkup()
@@ -268,11 +227,13 @@ class BotKeyboard:
return keyboard
@staticmethod
- def select_protocols(selected_protocols: Dict[str, List[str]],
- action: Literal["edit", "create", "create_from_template"],
- username: str = None,
- data_limit: float = None,
- expire_date: dt = None):
+ def select_services(services,
+ selected_services: List[int],
+ action: Literal["edit", "create"],
+ username: str = None,
+ data_limit: float = None,
+ expire_date: dt = None):
+
keyboard = types.InlineKeyboardMarkup()
if action == "edit":
@@ -308,28 +269,17 @@ class BotKeyboard:
callback_data=f"edit_user:{username}:expire"
)
)
-
- if action != 'create_from_template':
- for protocol, inbounds in xray.config.inbounds_by_protocol.items():
- keyboard.add(
+ for s in services:
+ keyboard.add(
types.InlineKeyboardButton(
- text=f"🌐 {protocol.upper()} {'✅' if protocol in selected_protocols else '❌'}",
- callback_data=f'select_protocol:{protocol}:{action}'
- )
- )
- if protocol in selected_protocols:
- for inbound in inbounds:
- keyboard.add(
- types.InlineKeyboardButton(
- text=f"«{inbound['tag']}» {'✅' if inbound['tag'] in selected_protocols[protocol] else '❌'}",
- callback_data=f'select_inbound:{inbound["tag"]}:{action}'
- )
+ text=f"🌐 {s.name} {'✅' if s.id in selected_services else '❌'}",
+ callback_data=f"switch_service:{s.id}:{action}"
)
-
+ )
keyboard.add(
types.InlineKeyboardButton(
text='Done',
- callback_data='confirm:edit_user' if action == "edit" else 'confirm:add_user'
+ callback_data='edit_user_done' if action == "edit" else 'add_user_done'
)
)
keyboard.add(
@@ -339,4 +289,4 @@ class BotKeyboard:
)
)
- return keyboard
\ No newline at end of file
+ return keyboard
diff --git a/app/views/system.py b/app/views/system.py
index b34a0f4..200de7d 100644
--- a/app/views/system.py
+++ b/app/views/system.py
@@ -50,7 +50,7 @@ def get_inbounds(db: Session = Depends(get_db), admin: Admin = Depends(Admin.get
return resp
-@app.get('/api/hosts', tags=["System"], response_model=Dict[str, List[InboundHost]])
+@app.get('/api/hosts', tags=["System"], response_model=Dict[int, List[InboundHost]])
def get_hosts(db: Session = Depends(get_db), admin: Admin = Depends(Admin.get_current)):
if not admin.is_sudo:
raise HTTPException(status_code=403, detail="You're not allowed")