This commit is contained in:
Corrado Mulas
2026-06-19 15:34:21 +02:00
parent 40c25aac4a
commit fea971f6fd
11 changed files with 109 additions and 67 deletions

View File

@@ -285,6 +285,7 @@ MONGODB_COLLECTION=ghconfig
STAFF_CHAT_ID=your_staff_chat_id
FACTORY_BOT_ID=your_bot_id
FACTORY_BOT_USERNAME=your_bot_username
```
---

View File

@@ -215,6 +215,7 @@ MONGODB_COLLECTION=ghconfig
# Admin
STAFF_CHAT_ID=your_admin_chat_id
FACTORY_BOT_ID=your_bot_id
FACTORY_BOT_USERNAME=your_bot_username
# Logging
LOG_LEVEL=INFO

View File

@@ -39,6 +39,7 @@ The application follows a modular architecture with the following components:
STAFF_CHAT_ID=your_admin_chat_id
FACTORY_BOT_ID=your_bot_id
FACTORY_BOT_USERNAME=your_bot_username
```
Generate `TELETHON_TOKEN` with:

View File

@@ -14,3 +14,4 @@ data:
TELETHON_API_ID: "123456"
STAFF_CHAT_ID: "-1001234567890"
FACTORY_BOT_ID: "123456789"
FACTORY_BOT_USERNAME: "grouphelpbot"

View File

@@ -24,6 +24,7 @@ async def create_group(payload: CreateGroupRequest, request: Request):
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"),
factory_bot_username=request.app.state.config["telegram"].get("factory_bot_username"),
)
return _envelope(message)

View File

@@ -9,7 +9,8 @@ TELETHON_TOKEN = os.environ.get("TELETHON_TOKEN")
TELETHON_API_HASH = os.environ.get("TELETHON_API_HASH")
TELETHON_API_ID = int(os.environ.get("TELETHON_API_ID", 0))
STAFF_CHAT_ID = int(os.environ.get("STAFF_CHAT_ID", 0))
FACTORY_BOT_ID = int(os.environ.get("FACTORY_BOT_ID", 0))
FACTORY_BOT_ID = int(os.environ.get("FACTORY_BOT_ID") or 0)
FACTORY_BOT_USERNAME = os.environ.get("FACTORY_BOT_USERNAME", "").strip()
# MongoDB configuration
MONGODB_URI = os.environ.get('MONGODB_URI', '')
@@ -74,7 +75,8 @@ def load_config():
'session': get_telegram_session(),
'session_file': 'session',
'staff_chat_id': STAFF_CHAT_ID,
'factory_bot_id': FACTORY_BOT_ID
'factory_bot_id': FACTORY_BOT_ID,
'factory_bot_username': FACTORY_BOT_USERNAME,
},
'mongodb': {
'uri': MONGODB_URI,

View File

@@ -14,6 +14,22 @@ class AdminHandler:
def __init__(self, user_service: UserService):
self.user_service = user_service
def _ensure_user_record(self, user_id: int) -> tuple:
user = self.user_service.get_user_by_id(user_id)
if user:
return user, False
user = User(id=user_id, username=None, first_name=f"id:{user_id}")
saved = self.user_service.save_user(user)
if not saved:
raise RuntimeError(f"Failed to create placeholder user record for {user_id}")
return user, True
def _format_user(self, user: User, user_id: int, created: bool = False) -> str:
created_note = " (placeholder created)" if created else ""
username = f"@{user.username}" if user.username else user.name
return f"{username} (ID: {user_id}){created_note}"
def verify_access(self, chat_id: int) -> tuple:
"""Verify admin access"""
@@ -53,19 +69,14 @@ class AdminHandler:
if not user_ids:
return "❌ Please provide at least one user ID.\n\nUsage: `/admin_set_users <user_id1> <user_id2> ...`"
# Verify all users exist
valid_user_ids = []
invalid_user_ids = []
created_users = []
for user_id in user_ids:
user = self.user_service.get_user_by_id(user_id)
if user:
valid_user_ids.append(user_id)
else:
invalid_user_ids.append(user_id)
if not valid_user_ids:
return f"❌ No valid users found. User IDs {invalid_user_ids} do not exist in database."
_, created = self._ensure_user_record(user_id)
valid_user_ids.append(user_id)
if created:
created_users.append(user_id)
# Set default users
if set_default_group_users(valid_user_ids):
@@ -73,12 +84,11 @@ class AdminHandler:
for user_id in valid_user_ids:
user = self.user_service.get_user_by_id(user_id)
if user:
user_list.append(f"{user.username} (ID: {user_id})")
user_list.append(self._format_user(user, user_id, user_id in created_users))
response = "✅ Default users updated successfully:\n" + "\n".join(user_list)
if invalid_user_ids:
response += f"\n\n⚠️ These users were not found: {invalid_user_ids}"
if created_users:
response += "\n\n Placeholder records were created for new Telegram IDs."
return response
else:
@@ -99,23 +109,18 @@ class AdminHandler:
current_users = get_default_group_users()
# Verify all new users exist
valid_user_ids = []
invalid_user_ids = []
created_users = []
for user_id in user_ids:
if user_id not in current_users:
user = self.user_service.get_user_by_id(user_id)
if user:
valid_user_ids.append(user_id)
else:
invalid_user_ids.append(user_id)
_, created = self._ensure_user_record(user_id)
valid_user_ids.append(user_id)
if created:
created_users.append(user_id)
if not valid_user_ids:
if invalid_user_ids:
return f"❌ No valid users found. User IDs {invalid_user_ids} do not exist in database."
else:
return " All provided users are already in the default list."
return " All provided users are already in the default list."
# Add to default users
updated_users = current_users + valid_user_ids
@@ -124,12 +129,11 @@ class AdminHandler:
for user_id in valid_user_ids:
user = self.user_service.get_user_by_id(user_id)
if user:
user_list.append(f"{user.username} (ID: {user_id})")
user_list.append(self._format_user(user, user_id, user_id in created_users))
response = "✅ Users added to default list:\n" + "\n".join(user_list)
if invalid_user_ids:
response += f"\n\n⚠️ These users were not found: {invalid_user_ids}"
if created_users:
response += "\n\n Placeholder records were created for new Telegram IDs."
return response
else:

View File

@@ -1,6 +1,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
from src.models.user import User
from src.services.user_service import UserService
logger = logging.getLogger(__name__)
@@ -10,6 +11,22 @@ class ConfigHandler:
def __init__(self, user_service: UserService):
self.user_service = user_service
def _ensure_user_record(self, user_id: int) -> tuple:
user = self.user_service.get_user_by_id(user_id)
if user:
return user, False
user = User(id=user_id, username=None, first_name=f"id:{user_id}")
saved = self.user_service.save_user(user)
if not saved:
raise RuntimeError(f"Failed to create placeholder user record for {user_id}")
return user, True
def _format_user(self, user: User, user_id: int, created: bool = False) -> str:
created_note = " (placeholder created)" if created else ""
username = f"@{user.username}" if user.username else user.name
return f"{username} (ID: {user_id}){created_note}"
async def handle_get_default_users(self) -> str:
"""Get current default users for new groups"""
@@ -37,19 +54,14 @@ class ConfigHandler:
if not user_ids:
return "❌ Please provide at least one user ID.\n\nUsage: `/set_default_users <user_id1> <user_id2> ...`"
# Verify all users exist
valid_user_ids = []
invalid_user_ids = []
created_users = []
for user_id in user_ids:
user = self.user_service.get_user_by_id(user_id)
if user:
valid_user_ids.append(user_id)
else:
invalid_user_ids.append(user_id)
if not valid_user_ids:
return f"❌ No valid users found. User IDs {invalid_user_ids} do not exist in database."
_, created = self._ensure_user_record(user_id)
valid_user_ids.append(user_id)
if created:
created_users.append(user_id)
# Set default users
if set_default_group_users(valid_user_ids):
@@ -57,12 +69,11 @@ class ConfigHandler:
for user_id in valid_user_ids:
user = self.user_service.get_user_by_id(user_id)
if user:
user_list.append(f"{user.username} (ID: {user_id})")
user_list.append(self._format_user(user, user_id, user_id in created_users))
response = "✅ Default users updated successfully:\n" + "\n".join(user_list)
if invalid_user_ids:
response += f"\n\n⚠️ These users were not found: {invalid_user_ids}"
if created_users:
response += "\n\n Placeholder records were created for new Telegram IDs."
return response
else:
@@ -79,23 +90,18 @@ class ConfigHandler:
current_users = get_default_group_users()
# Verify all new users exist
valid_user_ids = []
invalid_user_ids = []
created_users = []
for user_id in user_ids:
if user_id not in current_users:
user = self.user_service.get_user_by_id(user_id)
if user:
valid_user_ids.append(user_id)
else:
invalid_user_ids.append(user_id)
_, created = self._ensure_user_record(user_id)
valid_user_ids.append(user_id)
if created:
created_users.append(user_id)
if not valid_user_ids:
if invalid_user_ids:
return f"❌ No valid users found. User IDs {invalid_user_ids} do not exist in database."
else:
return " All provided users are already in the default list."
return " All provided users are already in the default list."
# Add to default users
updated_users = current_users + valid_user_ids
@@ -104,12 +110,11 @@ class ConfigHandler:
for user_id in valid_user_ids:
user = self.user_service.get_user_by_id(user_id)
if user:
user_list.append(f"{user.username} (ID: {user_id})")
user_list.append(self._format_user(user, user_id, user_id in created_users))
response = "✅ Users added to default list:\n" + "\n".join(user_list)
if invalid_user_ids:
response += f"\n\n⚠️ These users were not found: {invalid_user_ids}"
if created_users:
response += "\n\n Placeholder records were created for new Telegram IDs."
return response
else:

View File

@@ -22,6 +22,7 @@ class GroupHandler:
status_callback: Optional[StatusCallback] = None,
staff_chat_id: Optional[int] = None,
factory_bot_id: Optional[int] = None,
factory_bot_username: Optional[str] = None,
) -> str:
"""Handle command to create a new group"""
try:
@@ -32,6 +33,7 @@ class GroupHandler:
status_callback=status_callback,
staff_chat_id=staff_chat_id,
factory_bot_id=factory_bot_id,
factory_bot_username=factory_bot_username,
)
if result:
invite = f"\n🔗 {result['invite_link']}" if result.get("invite_link") else ""

View File

@@ -26,6 +26,7 @@ async def main():
config = load_config()
staff_chat_id = config['telegram']['staff_chat_id']
factory_bot_id = config['telegram']['factory_bot_id']
factory_bot_username = config['telegram']['factory_bot_username']
# Initialize MongoDB service
mongo_service = MongoDBService(
@@ -58,6 +59,7 @@ async def main():
status_callback=event.respond,
staff_chat_id=staff_chat_id,
factory_bot_id=factory_bot_id,
factory_bot_username=factory_bot_username,
)
await event.respond(response)

View File

@@ -106,6 +106,7 @@ class GroupService:
status_callback: Optional[StatusCallback] = None,
staff_chat_id: Optional[int] = None,
factory_bot_id: Optional[int] = None,
factory_bot_username: Optional[str] = None,
) -> Optional[dict]:
"""Create a Telegram megagroup and run the IMS GroupFactory setup flow."""
try:
@@ -165,6 +166,14 @@ class GroupService:
except UserPrivacyRestrictedError:
logger.warning(f"User {user.username or user.id} has privacy restrictions")
error_count += 1
except ValueError as e:
logger.error(f"Could not resolve Telegram entity for user {user.username or user.id}: {e}")
await self._notify(
status_callback,
f"⚠️ Could not resolve user {user.username or user.id}. "
"If this is a numeric ID, the userbot must have seen that user or you must store a username."
)
error_count += 1
except Exception as e:
logger.error(f"Error adding user {user.username or user.id}: {e}")
error_count += 1
@@ -178,10 +187,12 @@ class GroupService:
f"✅ Added {success_count}/{len(users)} users to the group",
)
if factory_bot_id:
factory_bot_username = (factory_bot_username or "").strip().lstrip("@")
factory_bot_ref = factory_bot_username or factory_bot_id
if factory_bot_ref:
try:
logger.info(f"Promoting factory bot {factory_bot_id} as manager")
factory_bot = await self.client.get_input_entity(factory_bot_id)
logger.info(f"Promoting factory bot {factory_bot_ref} as manager")
factory_bot = await self.client.get_input_entity(factory_bot_ref)
try:
await self.client(InviteToChannelRequest(target_group, [factory_bot]))
except Exception as e:
@@ -189,8 +200,15 @@ class GroupService:
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}")
logger.error(f"Failed to promote factory bot {factory_bot_ref}: {e}")
if factory_bot_username:
await self._notify(status_callback, f"⚠️ Failed to promote factory bot @{factory_bot_username}: {e}")
else:
await self._notify(
status_callback,
"⚠️ Failed to promote factory bot from FACTORY_BOT_ID. "
"Set FACTORY_BOT_USERNAME so Telethon can resolve the bot entity."
)
await self._notify(status_callback, "⚙️ Sending GroupHelp setup commands...")
await self.client.send_message(target_group, "/pro")
@@ -257,8 +275,12 @@ class GroupService:
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]))
try:
user_to_add = await self.client.get_input_entity(user_ref)
await self.client(InviteToChannelRequest(group, [user_to_add]))
except ValueError as e:
logger.error(f"Could not resolve Telegram entity for user {user_ref}: {e}")
return False
logger.info(f"Successfully added {len(users)} users to group {group_id}")
return True