Fix path
This commit is contained in:
16
README.md
16
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 <username> - 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 <name> - Create group with default users
|
||||
/create_group <name> - Deprecated; use !newgrp in staff chat
|
||||
/users - List all users
|
||||
/user <user_id> - Get user info
|
||||
/help - Show all available commands
|
||||
@@ -96,7 +105,8 @@ docker run telegram-groupfactory
|
||||
|
||||
## Available Commands
|
||||
|
||||
- `/create_group <name>` - Create a new group
|
||||
- `!newgrp` - Create a new group with DB users and stored GroupHelp QR import
|
||||
- `/create_group <name>` - Deprecated; use `!newgrp`
|
||||
- `/add_users <group_id> <user_ids>` - Add users to a group
|
||||
- `/get_group <group_id>` - Get group information
|
||||
- `/users` - List all users
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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})"
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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})"
|
||||
|
||||
192
src/main.py
192
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']
|
||||
)
|
||||
group_service.set_client(client)
|
||||
group_conversations = {}
|
||||
|
||||
@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
|
||||
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)
|
||||
|
||||
# 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)
|
||||
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 <name>` or `/create_group <name> <user_id1>,<user_id2>,...`")
|
||||
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 <name>` or `/create_group <name> <user_id1>,<user_id2>,...`")
|
||||
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 <user_id>` - Delete user
|
||||
|
||||
**Group Management:**
|
||||
• `/create_group <name>` - Create group with default users
|
||||
• `/create_group <name> <id1>,<id2>` - Create group with specific users
|
||||
• `!newgrp` - Create a group with database users and stored GroupHelp QR import
|
||||
• `/create_group` - Deprecated; use `!newgrp`
|
||||
• `/add_users <group_id> <id1>,<id2>` - Add users to group
|
||||
• `/get_group <group_id>` - Get group info
|
||||
|
||||
|
||||
@@ -13,6 +13,11 @@ class User:
|
||||
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):
|
||||
"""Create User instance from dictionary"""
|
||||
|
||||
@@ -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
|
||||
|
||||
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"""
|
||||
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,
|
||||
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
|
||||
|
||||
# Use default users if none specified
|
||||
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!')
|
||||
|
||||
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
|
||||
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
|
||||
|
||||
# Create group using Telegram API
|
||||
result = await self.client.create_group(group_name, [user.id for user in users])
|
||||
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",
|
||||
)
|
||||
|
||||
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
|
||||
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 <qr_data>`.")
|
||||
|
||||
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])
|
||||
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]))
|
||||
|
||||
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
|
||||
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,15 +274,13 @@ 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}")
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user