diff --git a/README.md b/README.md index a4a442d..2d5fdfb 100644 --- a/README.md +++ b/README.md @@ -75,6 +75,15 @@ docker run telegram-groupfactory ## Quick Start +### GroupFactory Commands (Staff Chat Only) +``` +!newgrp - Start interactive group creation +!confirm - Confirm pending group creation +!cancel - Cancel pending group creation +!PING - Check if the userbot is online +!help - Show GroupFactory commands +``` + ### Admin Commands (Admin Chat Only) ``` /admin_add_user - Add user to database @@ -86,9 +95,9 @@ docker run telegram-groupfactory /admin_get_qr - Retrieve GroupHelp backup QR data ``` -### User Commands +### Legacy User Commands ``` -/create_group - Create group with default users +/create_group - Deprecated; use !newgrp in staff chat /users - List all users /user - Get user info /help - Show all available commands @@ -96,7 +105,8 @@ docker run telegram-groupfactory ## Available Commands -- `/create_group ` - Create a new group +- `!newgrp` - Create a new group with DB users and stored GroupHelp QR import +- `/create_group ` - Deprecated; use `!newgrp` - `/add_users ` - Add users to a group - `/get_group ` - Get group information - `/users` - List all users diff --git a/src/api/routes/groups.py b/src/api/routes/groups.py index 032c373..65d91d1 100644 --- a/src/api/routes/groups.py +++ b/src/api/routes/groups.py @@ -6,7 +6,6 @@ from src.api.schemas import ( CommandResponse, CreateGroupRequest, ) -from src.config import save_user_admin_role def _envelope(message: str) -> CommandResponse: @@ -19,14 +18,13 @@ router = APIRouter(prefix="/api/groups", tags=["groups"], dependencies=[Depends( @router.post("", response_model=CommandResponse) async def create_group(payload: CreateGroupRequest, request: Request): handler = request.app.state.group_handler - message = await handler.handle_create_group(payload.name, payload.user_ids) - - # Mirror the Telegram inline-button flow: persist the admin-role choice - # against the bot user (the entity creating the group via the API). - if payload.full_admin is not None: - bot_user_id = request.app.state.config["telegram"].get("factory_bot_id") - if bot_user_id: - save_user_admin_role(bot_user_id, payload.full_admin) + message = await handler.handle_create_group( + payload.name, + user_ids=payload.user_ids, + description=payload.description or payload.name, + staff_chat_id=request.app.state.config["telegram"].get("staff_chat_id"), + factory_bot_id=request.app.state.config["telegram"].get("factory_bot_id"), + ) return _envelope(message) diff --git a/src/api/schemas.py b/src/api/schemas.py index 7d17343..722d1e8 100644 --- a/src/api/schemas.py +++ b/src/api/schemas.py @@ -15,6 +15,7 @@ class AddUserRequest(BaseModel): class CreateGroupRequest(BaseModel): name: str = Field(..., min_length=1) + description: Optional[str] = None user_ids: Optional[List[int]] = None full_admin: Optional[bool] = None diff --git a/src/handlers/admin_handler.py b/src/handlers/admin_handler.py index 5564c33..6214167 100644 --- a/src/handlers/admin_handler.py +++ b/src/handlers/admin_handler.py @@ -2,7 +2,7 @@ import logging from typing import List from src.config import ( get_default_group_users, set_default_group_users, get_qr_data, - set_qr_backup_data, verify_admin_access, save_user_admin_role + set_qr_backup_data, verify_admin_access ) from src.services.user_service import UserService from src.models.user import User @@ -191,7 +191,7 @@ class AdminHandler: import hashlib user_id = int(hashlib.md5(username.encode()).hexdigest()[:8], 16) - user = User(id=user_id, username=username, name=username) + user = User(id=user_id, username=username, first_name=username) success = self.user_service.save_user(user) if success: return f"āœ… User {username} added successfully (ID: {user_id})" diff --git a/src/handlers/group_handler.py b/src/handlers/group_handler.py index d822846..923b78c 100644 --- a/src/handlers/group_handler.py +++ b/src/handlers/group_handler.py @@ -1,10 +1,12 @@ import logging -from typing import List +from typing import Awaitable, Callable, List, Optional from src.services.user_service import UserService from src.services.group_service import GroupService logger = logging.getLogger(__name__) +StatusCallback = Callable[[str], Awaitable[object]] + class GroupHandler: """Handler class for group-related commands""" @@ -12,12 +14,34 @@ class GroupHandler: self.user_service = user_service self.group_service = group_service - async def handle_create_group(self, group_name: str, user_ids: List[int]) -> str: + async def handle_create_group( + self, + group_name: str, + user_ids: List[int] = None, + description: str = "", + status_callback: Optional[StatusCallback] = None, + staff_chat_id: Optional[int] = None, + factory_bot_id: Optional[int] = None, + ) -> str: """Handle command to create a new group""" try: - group_id = await self.group_service.create_group(group_name, user_ids) - if group_id: - return f"āœ… Group '{group_name}' created successfully with ID: {group_id}" + result = await self.group_service.create_group( + group_name, + description=description, + user_ids=user_ids, + status_callback=status_callback, + staff_chat_id=staff_chat_id, + factory_bot_id=factory_bot_id, + ) + if result: + invite = f"\nšŸ”— {result['invite_link']}" if result.get("invite_link") else "" + qr_status = "\nāœ… GroupHelp QR import sent" if result.get("qr_imported") else "\nāš ļø GroupHelp QR import not configured" + return ( + f"āœ… Group '{group_name}' created successfully with ID: {result['id']}\n" + f"šŸ‘„ Added {result['users_added']}/{result['users_total']} users" + f"{qr_status}" + f"{invite}" + ) else: return "āŒ Failed to create group" except Exception as e: @@ -46,4 +70,4 @@ class GroupHandler: return "āŒ Failed to retrieve group information" except Exception as e: logger.error(f"Error retrieving group info for {group_id}: {e}") - return f"āŒ Error retrieving group info: {str(e)}" \ No newline at end of file + return f"āŒ Error retrieving group info: {str(e)}" diff --git a/src/handlers/user_handler.py b/src/handlers/user_handler.py index ac4d8ec..ef6b622 100644 --- a/src/handlers/user_handler.py +++ b/src/handlers/user_handler.py @@ -43,7 +43,7 @@ class UserHandler: import hashlib user_id = int(hashlib.md5(username.encode()).hexdigest()[:8], 16) - user = User(id=user_id, username=username, name=username) + user = User(id=user_id, username=username, first_name=username) success = self.user_service.save_user(user) if success: return f"āœ… User {username} added successfully (ID: {user_id})" @@ -63,4 +63,4 @@ class UserHandler: return "āŒ Failed to delete user" except Exception as e: logger.error(f"Error deleting user {user_id}: {e}") - return f"āŒ Error deleting user: {str(e)}" \ No newline at end of file + return f"āŒ Error deleting user: {str(e)}" diff --git a/src/main.py b/src/main.py index 5c4cd54..fe11418 100644 --- a/src/main.py +++ b/src/main.py @@ -1,9 +1,8 @@ import asyncio import logging -import json import uvicorn -from telethon import Button, TelegramClient, events -from src.config import load_config, save_user_admin_role, get_user_admin_role +from telethon import TelegramClient, events +from src.config import load_config from src.services.mongodb_service import MongoDBService from src.services.user_service import UserService from src.services.group_service import GroupService @@ -25,6 +24,8 @@ async def main(): # Load configuration config = load_config() + staff_chat_id = config['telegram']['staff_chat_id'] + factory_bot_id = config['telegram']['factory_bot_id'] # Initialize MongoDB service mongo_service = MongoDBService( @@ -47,46 +48,21 @@ async def main(): config['telegram']['api_id'], config['telegram']['api_hash'] ) - - @client.on(events.CallbackQuery()) - async def callback_handler(event): - """Handle inline button callbacks for admin role selection""" - try: - data = event.data.decode() if isinstance(event.data, bytes) else event.data - - # Parse callback data - if data.startswith('admin_role:'): - user_id = event.sender_id - is_full_admin = data.split(':')[1] == 'yes' - - # Save the user's admin role preference - if save_user_admin_role(user_id, is_full_admin): - role_text = "āœ… Full Group Admin" if is_full_admin else "šŸ‘¤ Regular Member" - await event.answer(f"Set as {role_text}") - - # Edit the message to show the selection - await event.edit(f"Group Admin Role Selection\n\n{role_text} - Confirmed!") - else: - await event.answer("āŒ Failed to save preference", alert=True) - - elif data.startswith('group_create:'): - # Extract group name and user IDs from callback - parts = data.split(':', 2) - if len(parts) >= 3: - group_name = parts[1] - user_ids_str = parts[2] - - try: - user_ids = [int(uid) for uid in user_ids_str.split(',')] if user_ids_str else None - response = await group_handler.handle_create_group(group_name, user_ids) - await event.answer() - await event.edit(response) - except ValueError: - await event.answer("āŒ Invalid user IDs", alert=True) - - except Exception as e: - logger.error(f"Error handling callback: {e}") - await event.answer(f"āŒ Error: {str(e)}", alert=True) + group_service.set_client(client) + group_conversations = {} + + async def create_group_from_staff_flow(event, name: str, description: str): + response = await group_handler.handle_create_group( + name, + description=description, + status_callback=event.respond, + staff_chat_id=staff_chat_id, + factory_bot_id=factory_bot_id, + ) + await event.respond(response) + + def is_staff_chat(chat_id: int) -> bool: + return chat_id == staff_chat_id @client.on(events.NewMessage()) async def message_handler(event): @@ -101,6 +77,105 @@ async def main(): return try: + # ==================== IMS GROUPFACTORY STAFF FLOW ==================== + if is_staff_chat(chat_id): + raw_text = text.strip() + lower_text = raw_text.lower() + + if lower_text == '!ping': + await event.respond('PONG -> IMS GroupFactory Userbot') + return + + if lower_text == '!help': + help_text = """**IMS GroupFactory Commands** + +`!newgrp` - Start creating a new Telegram group +`!cancel` - Cancel the current operation +`!confirm` - Confirm group creation +`!PING` - Check if the userbot is online +`!help` - Display this help message""" + await event.respond(help_text) + return + + if lower_text == '!cancel': + if sender_id in group_conversations: + del group_conversations[sender_id] + await event.respond('šŸ›‘ Operation canceled.') + else: + await event.respond('ā“ No active operation to cancel.') + return + + if lower_text.startswith('!newgrp'): + if lower_text != '!newgrp': + cmd_args = raw_text[len('!newgrp'):].strip() + marker = "\n\nDescription:\n" + if marker in cmd_args: + name, description = cmd_args.split(marker, 1) + name = name.strip() + description = description.strip() + if not name or not description: + await event.respond('āš ļø Group name and description cannot be empty.') + return + await create_group_from_staff_flow(event, name, description) + else: + await event.respond('āš ļø Old format detected but missing description.\nUse `!newgrp` alone to start interactive mode.') + return + + group_conversations[sender_id] = { + 'step': 'name', + 'data': {} + } + await event.respond('šŸ‘‹ Let\'s create a new group!\n\nPlease enter the group name:') + return + + if sender_id in group_conversations: + current_step = group_conversations[sender_id]['step'] + + if lower_text == '!confirm' and current_step == 'confirm': + name = group_conversations[sender_id]['data']['name'] + description = group_conversations[sender_id]['data']['description'] + await event.respond('āœ… Confirmed! Starting group creation process...') + try: + await create_group_from_staff_flow(event, name, description) + finally: + group_conversations.pop(sender_id, None) + return + + if raw_text.startswith('!'): + if current_step == 'confirm': + await event.respond('ā“ Please type `!confirm` to create the group or `!cancel` to abort.') + return + + if current_step == 'name': + if not raw_text: + await event.respond('āš ļø Group name cannot be empty. Please try again:') + return + + group_conversations[sender_id]['data']['name'] = raw_text + group_conversations[sender_id]['step'] = 'description' + await event.respond(f'šŸ“ Group name set to: "{raw_text}"\n\nNow please enter the group description:') + return + + if current_step == 'description': + if not raw_text: + await event.respond('āš ļø Group description cannot be empty. Please try again:') + return + + group_conversations[sender_id]['data']['description'] = raw_text + name = group_conversations[sender_id]['data']['name'] + group_conversations[sender_id]['step'] = 'confirm' + await event.respond( + f'šŸ“‹ **Group Creation Summary**\n\n' + f'**Name**: {name}\n' + f'**Description**: {raw_text}\n\n' + f'Type `!confirm` to create this group or `!cancel` to abort.' + ) + return + + if current_step == 'confirm': + await event.respond('ā“ Please type `!confirm` to create the group or `!cancel` to abort.') + return + # ==================== ADMIN COMMANDS ==================== # All admin commands require being in the admin chat @@ -178,36 +253,7 @@ async def main(): # ==================== GROUP COMMANDS ==================== elif text.startswith('/create_group'): - # Parse group name and optional user IDs - parts = text.split(maxsplit=2) - if len(parts) > 1: - group_name = parts[1] - user_ids = None - if len(parts) > 2: - try: - user_ids = [int(uid) for uid in parts[2].split(',')] - except ValueError: - await event.respond("āŒ Invalid user IDs format.\n\nUsage: `/create_group ` or `/create_group ,,...`") - return - - # Create the group - response = await group_handler.handle_create_group(group_name, user_ids) - - # Ask about admin role with inline buttons - buttons = [ - [ - Button.inline("āœ… Yes, I want to be full admin", b"admin_role:yes"), - Button.inline("āŒ No, just regular member", b"admin_role:no"), - ] - ] - - await event.respond( - f"{response}\n\n" + - "šŸ‘¤ Would you like to be added as a full admin to this group?", - buttons=buttons - ) - else: - await event.respond("āŒ Please provide a group name.\n\nUsage: `/create_group ` or `/create_group ,,...`") + await event.respond("ā„¹ļø `/create_group` has been replaced. Use `!newgrp` in the staff chat.") elif text.startswith('/add_users'): # Parse group ID and user IDs @@ -287,8 +333,8 @@ async def main(): • `/delete_user ` - Delete user **Group Management:** -• `/create_group ` - Create group with default users -• `/create_group ,` - Create group with specific users +• `!newgrp` - Create a group with database users and stored GroupHelp QR import +• `/create_group` - Deprecated; use `!newgrp` • `/add_users ,` - Add users to group • `/get_group ` - Get group info diff --git a/src/models/user.py b/src/models/user.py index 94171da..d1f4dde 100644 --- a/src/models/user.py +++ b/src/models/user.py @@ -12,6 +12,11 @@ class User: def __post_init__(self): if self.id is None: raise ValueError("User ID cannot be None") + + @property + def name(self) -> str: + parts = [part for part in [self.first_name, self.last_name] if part] + return " ".join(parts) or self.username or str(self.id) @classmethod def from_dict(cls, data: dict): @@ -30,4 +35,4 @@ class User: 'username': self.username, 'first_name': self.first_name, 'last_name': self.last_name - } \ No newline at end of file + } diff --git a/src/services/group_service.py b/src/services/group_service.py index 2ed51a0..6f4f156 100644 --- a/src/services/group_service.py +++ b/src/services/group_service.py @@ -1,32 +1,135 @@ import logging -from typing import List, Optional -from src.models.user import User +import asyncio +from typing import Awaitable, Callable, List, Optional + +from telethon import functions, types +from telethon.errors.rpcerrorlist import PeerFloodError, UserPrivacyRestrictedError +from telethon.tl.functions.channels import CreateChannelRequest, InviteToChannelRequest + from src.services.mongodb_service import MongoDBService -from src.config import get_telegram_client, get_default_group_users +from src.config import get_default_group_users, get_qr_data logger = logging.getLogger(__name__) +StatusCallback = Callable[[str], Awaitable[object]] + class GroupService: """Service class for group creation and management""" - def __init__(self, mongo_service: MongoDBService): + def __init__(self, mongo_service: MongoDBService, telegram_client=None): self.mongo_service = mongo_service - self.client = get_telegram_client() + self.client = telegram_client + + def set_client(self, telegram_client): + self.client = telegram_client + + async def _notify(self, status_callback: Optional[StatusCallback], message: str): + if status_callback: + return await status_callback(message) + return None + + async def _edit_status(self, status_message, message: str): + if hasattr(status_message, "edit"): + await status_message.edit(message) + else: + await self.client.edit_message(status_message, message) + + def _admin_rights(self, **rights): + try: + return types.ChatAdminRights(**rights) + except TypeError: + rights.pop("manage_topics", None) + return types.ChatAdminRights(**rights) + + async def _promote_user(self, channel, user_id, full_admin: bool): + if full_admin: + admin_rights = self._admin_rights( + change_info=True, + post_messages=True, + edit_messages=True, + delete_messages=True, + ban_users=True, + invite_users=True, + pin_messages=True, + add_admins=True, + anonymous=False, + manage_call=True, + other=True, + manage_topics=True, + ) + rank = "manager" + else: + admin_rights = self._admin_rights( + change_info=False, + post_messages=True, + edit_messages=True, + delete_messages=True, + ban_users=True, + invite_users=True, + pin_messages=True, + add_admins=False, + anonymous=False, + manage_call=True, + other=False, + manage_topics=True, + ) + rank = "admin" + + await self.client(functions.channels.EditAdminRequest( + channel=channel, + user_id=user_id, + admin_rights=admin_rights, + rank=rank, + )) + + async def _export_invite_link(self, channel) -> Optional[str]: + try: + invite = await self.client(functions.messages.ExportChatInviteRequest( + peer=channel, + )) + return getattr(invite, "link", None) + except Exception as e: + logger.warning(f"Failed to export invite link: {e}") + return None + + def _build_importbackup_message(self, qr_data: str) -> str: + qr_data = qr_data.strip() + if qr_data.lower().startswith((".importbackup", "/importbackup")): + return qr_data + return f".importbackup {qr_data}" - async def create_group(self, group_name: str, user_ids: List[int] = None) -> Optional[str]: - """Create a Telegram group with specified users or default users""" + async def create_group( + self, + group_name: str, + description: str = "", + user_ids: List[int] = None, + status_callback: Optional[StatusCallback] = None, + staff_chat_id: Optional[int] = None, + factory_bot_id: Optional[int] = None, + ) -> Optional[dict]: + """Create a Telegram megagroup and run the IMS GroupFactory setup flow.""" try: if not self.client: logger.error("Telegram client not initialized") return None + + await self._notify(status_callback, f'šŸš€ Creating group "{group_name}"...') + created_channel = await self.client(CreateChannelRequest( + group_name, + description, + megagroup=True, + broadcast=False, + )) + + channel = created_channel.chats[0] + target_group = types.InputPeerChannel(channel.id, channel.access_hash) + await self._notify(status_callback, f'āœ… Group "{group_name}" created successfully!') - # Use default users if none specified if user_ids is None or len(user_ids) == 0: user_ids = get_default_group_users() if len(user_ids) == 0: logger.warning("No default users configured for group creation") - # Get users from MongoDB users = [] for user_id in user_ids: user = self.mongo_service.get_user_by_id(user_id) @@ -35,19 +138,98 @@ class GroupService: else: logger.warning(f"User {user_id} not found in database") - if not users: - logger.warning("No valid users found for group creation") - return None - - # Create group using Telegram API - result = await self.client.create_group(group_name, [user.id for user in users]) - - if result: - logger.info(f"Successfully created group '{group_name}' with {len(users)} members") - return result.id - else: - logger.error("Failed to create group") - return None + await self._notify(status_callback, f"šŸ‘„ Found {len(users)} configured users to add") + status_message = await self._notify(status_callback, "šŸ‘„ Adding users to the group...") + success_count = 0 + error_count = 0 + + for index, user in enumerate(users): + try: + if index % 5 == 0 and status_message: + await self._edit_status( + status_message, + f"šŸ‘„ Adding users: {index}/{len(users)} completed", + ) + + user_ref = user.username or user.id + user_to_add = await self.client.get_input_entity(user_ref) + await self.client(InviteToChannelRequest(target_group, [user_to_add])) + await self._promote_user(target_group, user_to_add, full_admin=False) + success_count += 1 + await asyncio.sleep(2) + + except PeerFloodError: + logger.warning("Telegram flood limit reached while adding users") + await self._notify(status_callback, "āš ļø Telegram flood limit reached. Pausing for 30 seconds...") + await asyncio.sleep(30) + except UserPrivacyRestrictedError: + logger.warning(f"User {user.username or user.id} has privacy restrictions") + error_count += 1 + except Exception as e: + logger.error(f"Error adding user {user.username or user.id}: {e}") + error_count += 1 + if error_count > 10: + await self._notify(status_callback, "āŒ Too many errors, aborting user addition!") + return None + + if status_message: + await self._edit_status( + status_message, + f"āœ… Added {success_count}/{len(users)} users to the group", + ) + + if factory_bot_id: + try: + logger.info(f"Promoting factory bot {factory_bot_id} as manager") + factory_bot = await self.client.get_input_entity(factory_bot_id) + try: + await self.client(InviteToChannelRequest(target_group, [factory_bot])) + except Exception as e: + logger.info(f"Factory bot invite skipped or failed before promotion: {e}") + await self._promote_user(target_group, factory_bot, full_admin=True) + await asyncio.sleep(2) + except Exception as e: + logger.error(f"Failed to promote factory bot {factory_bot_id}: {e}") + await self._notify(status_callback, f"āš ļø Failed to promote factory bot: {e}") + + await self._notify(status_callback, "āš™ļø Sending GroupHelp setup commands...") + await self.client.send_message(target_group, "/pro") + await asyncio.sleep(2) + if staff_chat_id: + await self.client.send_message(target_group, f"/setstaffgroup {staff_chat_id}") + await asyncio.sleep(2) + + qr_imported = False + qr_data = get_qr_data() + if qr_data: + logger.info("Sending GroupHelp QR backup import command") + await self.client.send_message( + target_group, + self._build_importbackup_message(qr_data), + ) + qr_imported = True + if staff_chat_id: + await self.client.send_message(staff_chat_id, "āœ… GroupHelp QR backup import sent to the new group.") + elif staff_chat_id: + await self.client.send_message(staff_chat_id, "āš ļø No GroupHelp QR backup data configured. Use `/admin_set_qr `.") + + invite_link = await self._export_invite_link(target_group) + if staff_chat_id: + if invite_link: + await self.client.send_message(staff_chat_id, f"šŸ”— Invite link:\n\n{invite_link}") + else: + await self.client.send_message(staff_chat_id, "āš ļø Failed to generate invite link") + + logger.info(f"Group creation process completed for {group_name}") + return { + "id": channel.id, + "access_hash": channel.access_hash, + "title": channel.title, + "invite_link": invite_link, + "users_added": success_count, + "users_total": len(users), + "qr_imported": qr_imported, + } except Exception as e: logger.error(f"Error creating group '{group_name}': {e}") @@ -60,7 +242,6 @@ class GroupService: logger.error("Telegram client not initialized") return False - # Get users from MongoDB users = [] for user_id in user_ids: user = self.mongo_service.get_user_by_id(user_id) @@ -73,15 +254,14 @@ class GroupService: logger.warning("No valid users found for adding to group") return False - # Add users to group using Telegram API - result = await self.client.add_users_to_group(group_id, [user.id for user in users]) - - if result: - logger.info(f"Successfully added {len(users)} users to group {group_id}") - return True - else: - logger.error("Failed to add users to group") - return False + group = await self.client.get_input_entity(group_id) + for user in users: + user_ref = user.username or user.id + user_to_add = await self.client.get_input_entity(user_ref) + await self.client(InviteToChannelRequest(group, [user_to_add])) + + logger.info(f"Successfully added {len(users)} users to group {group_id}") + return True except Exception as e: logger.error(f"Error adding users to group {group_id}: {e}") @@ -94,16 +274,14 @@ class GroupService: logger.error("Telegram client not initialized") return None - # Get group info using Telegram API - result = await self.client.get_group_info(group_id) - - if result: - logger.info(f"Retrieved information for group {group_id}") - return result - else: - logger.warning(f"Failed to retrieve information for group {group_id}") - return None + result = await self.client.get_entity(group_id) + logger.info(f"Retrieved information for group {group_id}") + return { + "id": getattr(result, "id", None), + "title": getattr(result, "title", None), + "username": getattr(result, "username", None), + } except Exception as e: logger.error(f"Error retrieving group info for {group_id}: {e}") - return None \ No newline at end of file + return None diff --git a/src/services/mongodb_service.py b/src/services/mongodb_service.py index d612ca6..e43bfa5 100644 --- a/src/services/mongodb_service.py +++ b/src/services/mongodb_service.py @@ -33,7 +33,7 @@ class MongoDBService: def get_users(self) -> List[User]: """Retrieve all users from MongoDB collection""" try: - if not self.collection: + if self.collection is None: logger.error("MongoDB collection not initialized") return [] @@ -48,7 +48,7 @@ class MongoDBService: def get_user_by_id(self, user_id: int) -> Optional[User]: """Retrieve a specific user by ID from MongoDB""" try: - if not self.collection: + if self.collection is None: logger.error("MongoDB collection not initialized") return None @@ -63,7 +63,7 @@ class MongoDBService: def save_user(self, user: User) -> bool: """Save a user to MongoDB collection""" try: - if not self.collection: + if self.collection is None: logger.error("MongoDB collection not initialized") return False @@ -84,7 +84,7 @@ class MongoDBService: def delete_user(self, user_id: int) -> bool: """Delete a user from MongoDB collection""" try: - if not self.collection: + if self.collection is None: logger.error("MongoDB collection not initialized") return False @@ -99,4 +99,4 @@ class MongoDBService: """Close MongoDB connection""" if self.client: self.client.close() - logger.info("Closed MongoDB connection") \ No newline at end of file + logger.info("Closed MongoDB connection")