s
This commit is contained in:
121
bot/main.py
121
bot/main.py
@@ -253,19 +253,116 @@ async def show_my_accounts(callback: CallbackQuery):
|
|||||||
reply_markup=main_menu_keyboard(is_admin(callback.from_user.id))
|
reply_markup=main_menu_keyboard(is_admin(callback.from_user.id))
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
text = "Your SIP accounts:\n\n"
|
keyboard = InlineKeyboardMarkup(inline_keyboard=[])
|
||||||
|
|
||||||
for acc in accounts:
|
for acc in accounts:
|
||||||
text += f"Number: {acc['extension_number']}\n"
|
keyboard.inline_keyboard.append([
|
||||||
if acc['sip_secret']:
|
InlineKeyboardButton(
|
||||||
text += f"Password: {acc['sip_secret']}\n"
|
text=f"📞 {acc['extension_number']}",
|
||||||
text += f"Created: {acc['created_at']}\n\n"
|
callback_data=f"my_account_{acc['extension_number']}"
|
||||||
|
)
|
||||||
|
])
|
||||||
|
|
||||||
|
keyboard.inline_keyboard.append([
|
||||||
|
InlineKeyboardButton(text="Back", callback_data="back_main")
|
||||||
|
])
|
||||||
|
|
||||||
await callback.message.edit_text(
|
await callback.message.edit_text(
|
||||||
text,
|
"Your SIP accounts. Select one to view details:",
|
||||||
reply_markup=main_menu_keyboard(is_admin(callback.from_user.id))
|
reply_markup=keyboard
|
||||||
)
|
)
|
||||||
await callback.answer()
|
await callback.answer()
|
||||||
|
|
||||||
|
@dp.callback_query(F.data.startswith("my_account_"))
|
||||||
|
async def show_my_account_details(callback: CallbackQuery):
|
||||||
|
number = callback.data.replace("my_account_", "")
|
||||||
|
account = await db.get_sip_account_by_number(number)
|
||||||
|
|
||||||
|
if not account:
|
||||||
|
await callback.answer("Account not found.")
|
||||||
|
return
|
||||||
|
|
||||||
|
text = (
|
||||||
|
f"Account: {account['extension_number']}\n"
|
||||||
|
f"Password: {account['sip_secret'] or 'Not set'}\n"
|
||||||
|
f"Created: {account['created_at']}\n\n"
|
||||||
|
"What would you like to do?"
|
||||||
|
)
|
||||||
|
|
||||||
|
keyboard = InlineKeyboardMarkup(inline_keyboard=[
|
||||||
|
[InlineKeyboardButton(text="Show connection parameters", callback_data=f"show_params_{number}")],
|
||||||
|
[InlineKeyboardButton(text="Delete this account", callback_data=f"delete_my_account_{number}")],
|
||||||
|
[InlineKeyboardButton(text="Back to my accounts", callback_data="my_accounts")]
|
||||||
|
])
|
||||||
|
|
||||||
|
await callback.message.edit_text(text, reply_markup=keyboard)
|
||||||
|
await callback.answer()
|
||||||
|
|
||||||
|
@dp.callback_query(F.data.startswith("show_params_"))
|
||||||
|
async def show_connection_parameters(callback: CallbackQuery):
|
||||||
|
number = callback.data.replace("show_params_", "")
|
||||||
|
account = await db.get_sip_account_by_number(number)
|
||||||
|
|
||||||
|
if not account:
|
||||||
|
await callback.answer("Account not found.")
|
||||||
|
return
|
||||||
|
|
||||||
|
connection_info = (
|
||||||
|
f"Connection parameters for {number}:\n\n"
|
||||||
|
f"Domain: {DEFAULT_SIP_DOMAIN}\n"
|
||||||
|
f"Server: {DEFAULT_SIP_SERVER}\n"
|
||||||
|
f"Port: {DEFAULT_SIP_PORT}\n"
|
||||||
|
f"Transport: {DEFAULT_SIP_TRANSPORT}\n"
|
||||||
|
f"Username: {number}\n"
|
||||||
|
f"Password: {account['sip_secret']}\n"
|
||||||
|
)
|
||||||
|
|
||||||
|
if DEFAULT_SIP_OUTBOUND_PROXY:
|
||||||
|
connection_info += f"Outbound Proxy: {DEFAULT_SIP_OUTBOUND_PROXY}\n"
|
||||||
|
if DEFAULT_SIP_STUN:
|
||||||
|
connection_info += f"STUN: {DEFAULT_SIP_STUN}\n"
|
||||||
|
|
||||||
|
connection_info += "\nRecommended clients: Zoiper, MicroSIP, Linphone"
|
||||||
|
|
||||||
|
keyboard = InlineKeyboardMarkup(inline_keyboard=[
|
||||||
|
[InlineKeyboardButton(text="Back", callback_data=f"my_account_{number}")]
|
||||||
|
])
|
||||||
|
|
||||||
|
await callback.message.edit_text(connection_info, reply_markup=keyboard)
|
||||||
|
await callback.answer()
|
||||||
|
|
||||||
|
@dp.callback_query(F.data.startswith("delete_my_account_"))
|
||||||
|
async def confirm_delete_my_account(callback: CallbackQuery):
|
||||||
|
number = callback.data.replace("delete_my_account_", "")
|
||||||
|
|
||||||
|
keyboard = InlineKeyboardMarkup(inline_keyboard=[
|
||||||
|
[InlineKeyboardButton(text="Yes, delete", callback_data=f"confirm_delete_my_{number}")],
|
||||||
|
[InlineKeyboardButton(text="Cancel", callback_data=f"my_account_{number}")]
|
||||||
|
])
|
||||||
|
|
||||||
|
await callback.message.edit_text(
|
||||||
|
f"Are you sure you want to delete extension {number}?\n"
|
||||||
|
"This action cannot be undone.",
|
||||||
|
reply_markup=keyboard
|
||||||
|
)
|
||||||
|
await callback.answer()
|
||||||
|
|
||||||
|
@dp.callback_query(F.data.startswith("confirm_delete_my_"))
|
||||||
|
async def delete_my_account(callback: CallbackQuery):
|
||||||
|
number = callback.data.replace("confirm_delete_my_", "")
|
||||||
|
|
||||||
|
# Delete from MikoPBX
|
||||||
|
await mikopbx.delete_extension(number)
|
||||||
|
|
||||||
|
# Delete from local DB
|
||||||
|
await db.delete_sip_account(number)
|
||||||
|
|
||||||
|
await callback.message.edit_text(
|
||||||
|
f"Extension {number} has been deleted.",
|
||||||
|
reply_markup=main_menu_keyboard(is_admin(callback.from_user.id))
|
||||||
|
)
|
||||||
|
await callback.answer()
|
||||||
|
|
||||||
# ============== ADMIN HANDLERS ==============
|
# ============== ADMIN HANDLERS ==============
|
||||||
|
|
||||||
@dp.callback_query(F.data == "admin_menu")
|
@dp.callback_query(F.data == "admin_menu")
|
||||||
@@ -406,12 +503,20 @@ async def admin_view_account(callback: CallbackQuery):
|
|||||||
await callback.answer("Account not found.")
|
await callback.answer("Account not found.")
|
||||||
return
|
return
|
||||||
|
|
||||||
|
# Get owner info
|
||||||
|
owner = await db.get_user(account.get('telegram_id', 0)) if account.get('telegram_id') else None
|
||||||
|
owner_info = ""
|
||||||
|
if owner:
|
||||||
|
owner_info = f"Owner: @{owner.get('telegram_username', 'N/A')} (ID: {owner['telegram_id']})\n"
|
||||||
|
else:
|
||||||
|
owner_info = f"Owner Telegram ID: {account.get('telegram_id', 'N/A')}\n"
|
||||||
|
|
||||||
text = (
|
text = (
|
||||||
f"Account details:\n\n"
|
f"Account details:\n\n"
|
||||||
f"Number: {account['extension_number']}\n"
|
f"Number: {account['extension_number']}\n"
|
||||||
f"Password: {account['sip_secret'] or 'Not set'}\n"
|
f"Password: {account['sip_secret'] or 'Not set'}\n"
|
||||||
f"Username: {account.get('username', 'N/A')}\n"
|
f"Username: {account.get('username', 'N/A')}\n"
|
||||||
f"Owner Telegram ID: {account.get('telegram_id', 'N/A')}\n"
|
f"{owner_info}"
|
||||||
f"Created: {account['created_at']}\n\n"
|
f"Created: {account['created_at']}\n\n"
|
||||||
f"Actions:"
|
f"Actions:"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -129,22 +129,55 @@ class MikoPBXService:
|
|||||||
"error": error
|
"error": error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async def get_employee_by_number(self, number: str) -> Optional[Dict]:
|
||||||
|
"""Get employee record by extension number (returns full record with real 'id')"""
|
||||||
|
# Get list and search by number
|
||||||
|
response = await self._make_request("GET", "/pbxcore/api/v3/employees")
|
||||||
|
if not response.get("result"):
|
||||||
|
return None
|
||||||
|
|
||||||
|
employees = response.get("data", [])
|
||||||
|
for emp in employees:
|
||||||
|
if isinstance(emp, dict) and emp.get("number") == number:
|
||||||
|
return emp
|
||||||
|
return None
|
||||||
|
|
||||||
async def get_extension(self, number: str) -> Optional[Dict]:
|
async def get_extension(self, number: str) -> Optional[Dict]:
|
||||||
"""Get employee by number"""
|
"""Get employee by number"""
|
||||||
response = await self._make_request("GET", f"/pbxcore/api/v3/employees/{number}")
|
return await self.get_employee_by_number(number)
|
||||||
return response.get("data") if response.get("result") else None
|
|
||||||
|
|
||||||
async def set_extension_secret(self, number: str, new_secret: str) -> Dict[str, Any]:
|
async def set_extension_secret(self, number: str, new_secret: str) -> Dict[str, Any]:
|
||||||
current = await self.get_extension(number)
|
current = await self.get_employee_by_number(number)
|
||||||
if not current:
|
if not current:
|
||||||
return {"success": False, "error": "Extension not found"}
|
return {"success": False, "error": "Extension not found"}
|
||||||
|
|
||||||
|
real_id = current.get("id")
|
||||||
current["sip_secret"] = new_secret
|
current["sip_secret"] = new_secret
|
||||||
response = await self._make_request("PUT", f"/pbxcore/api/v3/employees/{number}", json_data=current)
|
|
||||||
|
response = await self._make_request(
|
||||||
|
"PUT",
|
||||||
|
f"/pbxcore/api/v3/employees/{real_id}",
|
||||||
|
json_data=current
|
||||||
|
)
|
||||||
return {"success": response.get("result", False), "messages": response.get("messages", [])}
|
return {"success": response.get("result", False), "messages": response.get("messages", [])}
|
||||||
|
|
||||||
async def delete_extension(self, number: str) -> Dict[str, Any]:
|
async def delete_extension(self, number: str) -> Dict[str, Any]:
|
||||||
response = await self._make_request("DELETE", f"/pbxcore/api/v3/employees/{number}")
|
"""Delete employee by extension number (finds real ID first)"""
|
||||||
return {"success": response.get("result", False), "messages": response.get("messages", [])}
|
employee = await self.get_employee_by_number(number)
|
||||||
|
if not employee:
|
||||||
|
return {"success": False, "error": "Employee not found"}
|
||||||
|
|
||||||
|
real_id = employee.get("id")
|
||||||
|
logger.info(f">>> Deleting employee with real ID: {real_id}")
|
||||||
|
|
||||||
|
response = await self._make_request(
|
||||||
|
"DELETE",
|
||||||
|
f"/pbxcore/api/v3/employees/{real_id}"
|
||||||
|
)
|
||||||
|
return {
|
||||||
|
"success": response.get("result", False),
|
||||||
|
"messages": response.get("messages", [])
|
||||||
|
}
|
||||||
|
|
||||||
async def close(self):
|
async def close(self):
|
||||||
if self.session:
|
if self.session:
|
||||||
|
|||||||
Reference in New Issue
Block a user