Files
reverse-skills-max/research-what-we-did/deskgram_reverse.py

638 lines
30 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""
Deskgram 2 — реконструкция функций по параметрам из веб-превью.
Telethon-based. Все параметры взяты из интерфейса модулей.
"""
import asyncio, random, json, os, re, time, csv
from datetime import datetime, timedelta
from pathlib import Path
from telethon import TelegramClient, functions, types, errors
from telethon.tl.types import InputPeerUser, InputPeerChat, InputPeerChannel
from telethon.tl.functions.messages import GetDialogsRequest
from telethon.tl.functions.channels import InviteToChannelRequest, JoinChannelRequest
from telethon.tl.functions.account import UpdateProfileRequest, UpdateUsernameRequest
from telethon.tl.functions.photos import UploadProfilePhotoRequest
from telethon.sessions import StringSession
from dataclasses import dataclass, field
from typing import Optional
# ═══════════════════════════════════════════════════════════════
# МОДУЛЬ: Панель аккаунтов
# ═══════════════════════════════════════════════════════════════
async def account_check(client: TelegramClient):
"""Проверка аккаунта: статус, спамблок, премиум"""
me = await client.get_me()
try:
await client.send_message('SpamBot', '/start')
await asyncio.sleep(2)
msgs = await client.get_messages('SpamBot', limit=1)
spam_blocked = 'ограничен' in (msgs[0].message or '').lower()
except:
spam_blocked = None
return {
'id': me.id, 'username': me.username, 'phone': me.phone,
'premium': getattr(me, 'premium', False),
'spam_blocked': spam_blocked,
'last_seen': me.status.was_online if hasattr(me.status, 'was_online') else None
}
async def account_edit_profile(client: TelegramClient, *,
first_name=None, last_name=None, bio=None, username=None, photo_path=None):
"""Редактирование профиля аккаунта"""
if first_name or last_name:
await client(UpdateProfileRequest(
first_name=first_name, last_name=last_name, about=bio))
if username:
await client(UpdateUsernameRequest(username))
if photo_path:
await client(UploadProfilePhotoRequest(
await client.upload_file(photo_path)))
async def account_set_2fa(client: TelegramClient, password: str, hint: str = ''):
"""Установка 2FA"""
await client.edit_2fa(new_password=password, hint=hint)
async def account_get_sessions(client: TelegramClient):
"""Получить активные сессии"""
return await client(functions.account.GetAuthorizationsRequest())
async def account_terminate_sessions(client: TelegramClient):
"""Сбросить все сессии кроме текущей"""
await client(functions.auth.ResetAuthorizationsRequest())
async def account_privacy(client: TelegramClient, *,
hide_phone=True, hide_lastseen=True, hide_forwarded=True):
"""Настройки приватности"""
rules = []
if hide_phone:
rules.append(types.InputPrivacyValueDisallowAll())
if hide_lastseen:
pass # аналогично через account.setPrivacy
await client(functions.account.SetPrivacyRequest(
key=types.InputPrivacyKeyPhoneNumber(), rules=rules))
# ═══════════════════════════════════════════════════════════════
# МОДУЛЬ: Рассылка в ЛС
# ═══════════════════════════════════════════════════════════════
@dataclass
class SpamPMConfig:
threads: int = 3
msg_per_account: int = 50
delay_min: float = 30.0
delay_max: float = 90.0
silent: bool = False
ttl_seconds: Optional[int] = None
pin_message: bool = False
delete_sender: bool = False
block_after: bool = False
add_to_blacklist: bool = False
remove_links_preview: bool = False
rotate_on_flood: bool = True
check_spambot: bool = True
use_autoresponder: bool = False
collect_incoming: bool = False
thread_start_delay: tuple = (5, 30)
async def spam_pm(client: TelegramClient, users: list, message: str, cfg: SpamPMConfig):
"""Рассылка в ЛС с настройками из интерфейса"""
sent = 0
for user in users:
if sent >= cfg.msg_per_account:
break
try:
entity = await client.get_input_entity(user)
await client.send_message(
entity, message,
silent=cfg.silent,
background=None
)
sent += 1
if cfg.block_after:
await client(functions.contacts.BlockRequest(entity))
delay = random.uniform(cfg.delay_min, cfg.delay_max)
await asyncio.sleep(delay)
except errors.FloodWaitError as e:
if cfg.rotate_on_flood:
return False # сигнал сменить аккаунт
await asyncio.sleep(e.seconds)
except errors.PeerFloodError:
if cfg.check_spambot:
await client.send_message('SpamBot', '/start')
return False
except Exception:
continue
return True
# ═══════════════════════════════════════════════════════════════
# МОДУЛЬ: Нейрочаттинг
# ═══════════════════════════════════════════════════════════════
@dataclass
class NeuroChatConfig:
mode: str = 'keywords' # keywords | trigger | smart | promoter
chat_source: str = 'subscriptions' # subscriptions | list
chat_list: list = field(default_factory=list)
replies_per_account: int = 30
context_messages: int = 5
delay_before_reply: tuple = (5, 30)
pause_between_replies: tuple = (60, 300)
max_replies_per_chat: int = 10
chat_cooldown_min: int = 30
trigger_words: list = field(default_factory=list)
whole_word: bool = True
case_sensitive: bool = False
ignore_bots: bool = True
ignore_system: bool = True
min_message_length: int = 1
leave_if_cant_write: bool = False
auto_leave_spam: bool = False
respond_to_our_replies: bool = False
check_spambot_on_flood: bool = True
async def neurochatting(client: TelegramClient, ai_generate, cfg: NeuroChatConfig):
"""Нейрочаттинг: AI отвечает на сообщения в чатах"""
if cfg.chat_source == 'subscriptions':
dialogs = await client.get_dialogs()
chats = [d for d in dialogs if d.is_group]
else:
chats = cfg.chat_list
chat_stats = {}
for chat in chats:
if chat_stats.get(chat.id, 0) >= cfg.max_replies_per_chat:
continue
try:
messages = await client.get_messages(chat, limit=50)
except errors.ChatWriteForbiddenError:
if cfg.leave_if_cant_write:
await client.delete_dialog(chat)
continue
for msg in messages:
if not msg.message:
continue
if cfg.ignore_bots and msg.sender and getattr(msg.sender, 'bot', False):
continue
if cfg.ignore_system and getattr(msg, 'action', None):
continue
if len(msg.message or '') < cfg.min_message_length:
continue
# Проверка триггеров
should_reply = False
text = msg.message or ''
if cfg.mode == 'keywords':
for word in cfg.trigger_words:
if cfg.whole_word:
pattern = rf'\b{re.escape(word)}\b'
else:
pattern = re.escape(word)
flags = 0 if cfg.case_sensitive else re.IGNORECASE
if re.search(pattern, text, flags):
should_reply = True
break
elif cfg.mode == 'trigger':
should_reply = True # AI решает
elif cfg.mode == 'smart':
should_reply = True
elif cfg.mode == 'promoter':
should_reply = True
if should_reply:
# Контекст
context = await client.get_messages(
chat, limit=cfg.context_messages, offset_id=msg.id)
context_text = '\n'.join(
f"[{m.sender_id}]: {m.message}" for m in reversed(context) if m.message)
# AI генерация
reply = await ai_generate(context_text, msg.message)
delay = random.uniform(*cfg.delay_before_reply)
await asyncio.sleep(delay)
try:
await client.send_message(chat, reply, reply_to=msg.id)
chat_stats[chat.id] = chat_stats.get(chat.id, 0) + 1
except errors.PeerFloodError:
if cfg.check_spambot_on_flood:
await client.send_message('SpamBot', '/start')
return chat_stats
pause = random.uniform(*cfg.pause_between_replies)
await asyncio.sleep(pause)
return chat_stats
# ═══════════════════════════════════════════════════════════════
# МОДУЛЬ: Инвайтинг
# ═══════════════════════════════════════════════════════════════
async def invite_users(client: TelegramClient, chat, users: list,
per_account: int = 50, delay: tuple = (60, 180), rotate_on_flood: bool = True):
"""Массовый инвайт пользователей в канал/группу"""
invited = 0
for user in users:
if invited >= per_account:
break
try:
entity = await client.get_input_entity(user)
await client(InviteToChannelRequest(chat, [entity]))
invited += 1
await asyncio.sleep(random.uniform(*delay))
except errors.FloodWaitError as e:
if rotate_on_flood:
return invited
await asyncio.sleep(e.seconds)
except errors.UserPrivacyRestrictedError:
continue
except Exception:
continue
return invited
# ═══════════════════════════════════════════════════════════════
# МОДУЛЬ: Парсер аудитории
# ═══════════════════════════════════════════════════════════════
async def parse_chat_members(client: TelegramClient, chat,
limit: int = 50000, active_only: bool = False) -> list[dict]:
"""Сбор участников чата/канала"""
users = []
try:
async for member in client.iter_participants(chat, limit=limit):
if active_only and member.status:
if hasattr(member.status, 'was_online'):
if (datetime.now() - member.status.was_online).days > 30:
continue
users.append({
'id': member.id,
'username': member.username,
'first_name': member.first_name,
'last_name': member.last_name,
'phone': getattr(member, 'phone', None),
'premium': getattr(member, 'premium', False),
'bot': member.bot,
'was_online': str(getattr(member.status, 'was_online', None))
})
except errors.ChatAdminRequiredError:
pass
return users
async def parse_comments(client: TelegramClient, channel, post_limit: int = 100):
"""Парсер комментаторов постов"""
commenters = {}
messages = await client.get_messages(channel, limit=post_limit)
for msg in messages:
if msg.replies and msg.replies.replies > 0:
try:
async for comment in client.iter_messages(
channel, reply_to=msg.id, limit=100):
if comment.sender_id and comment.sender_id not in commenters:
commenters[comment.sender_id] = {
'user_id': comment.sender_id,
'post_id': msg.id,
'count': 1
}
elif comment.sender_id:
commenters[comment.sender_id]['count'] += 1
except:
continue
return sorted(commenters.values(), key=lambda x: x['count'], reverse=True)
# ═══════════════════════════════════════════════════════════════
# МОДУЛЬ: Нейрокомментинг
# ═══════════════════════════════════════════════════════════════
async def neuro_commenting(client: TelegramClient, channels: list,
ai_generate, posts_per_channel: int = 10,
delay: tuple = (30, 120), mode: str = 'firstnah'):
"""Автокомментирование новых постов с AI"""
results = []
for channel in channels:
try:
# Первонах (firstnah) — комментарий первым
messages = await client.get_messages(channel, limit=posts_per_channel)
for msg in messages:
if not msg.message:
continue
try:
existing = await client.get_messages(
channel, reply_to=msg.id, limit=5)
our_comments = [c for c in existing
if c.sender_id == (await client.get_me()).id]
if mode == 'firstnah' and our_comments:
continue # уже комментили
except:
pass
comment = await ai_generate(
f"POST: {msg.message[:500]}",
style='positive' # из настроек модуля
)
await asyncio.sleep(random.uniform(*delay))
await client.send_message(channel, comment, reply_to=msg.id)
results.append({'channel': channel, 'post_id': msg.id, 'comment': comment[:100]})
except Exception as e:
results.append({'channel': channel, 'error': str(e)})
return results
# ═══════════════════════════════════════════════════════════════
# МОДУЛЬ: Рассылка в группы
# ═══════════════════════════════════════════════════════════════
async def spam_groups(client: TelegramClient, chats: list, message: str,
ai_rewrite=False, ai_generate=None,
per_account: int = 20, delay: tuple = (60, 180)):
"""Рассылка в группы с AI-рерайтом"""
sent = 0
for chat in chats:
if sent >= per_account:
break
try:
text = message
if ai_rewrite and ai_generate:
text = await ai_generate(f"Rewrite this naturally: {message}")
await client.send_message(chat, text)
sent += 1
await asyncio.sleep(random.uniform(*delay))
except errors.FloodWaitError as e:
await asyncio.sleep(e.seconds)
except Exception:
continue
return sent
# ═══════════════════════════════════════════════════════════════
# МОДУЛЬ: Автоответчик с ветвлением
# ═══════════════════════════════════════════════════════════════
@dataclass
class AutoReplyRule:
trigger_words: list
response: str
whole_word: bool = True
case_sensitive: bool = False
collect_usernames: bool = False
async def auto_responder(client: TelegramClient, rules: list[AutoReplyRule],
collect_usernames: bool = False, blacklist: set = set()):
"""Автоответчик на входящие сообщения"""
collected = []
while True:
try:
dialogs = await client.get_dialogs()
for dialog in dialogs:
if dialog.unread_count > 0 and dialog.id not in blacklist:
msgs = await client.get_messages(dialog.id, limit=dialog.unread_count)
for msg in msgs:
if msg.out:
continue
if collect_usernames and msg.sender:
collected.append({
'user_id': msg.sender_id,
'username': getattr(msg.sender, 'username', None),
'message': msg.message[:200]
})
text = msg.message or ''
for rule in rules:
for word in rule.trigger_words:
if rule.whole_word:
pattern = rf'\b{re.escape(word)}\b'
else:
pattern = re.escape(word)
flags = 0 if rule.case_sensitive else re.IGNORECASE
if re.search(pattern, text, flags):
await client.send_message(dialog.id, rule.response)
break
else:
continue
break
except Exception:
pass
await asyncio.sleep(5)
# ═══════════════════════════════════════════════════════════════
# МОДУЛЬ: Массовые подписки + лайки + просмотры
# ═══════════════════════════════════════════════════════════════
async def mass_join(client: TelegramClient, channels: list,
per_account: int = 50, delay: tuple = (30, 90)):
"""Массовое вступление в каналы/группы"""
joined = 0
for ch in channels:
if joined >= per_account:
break
try:
await client(JoinChannelRequest(ch))
joined += 1
await asyncio.sleep(random.uniform(*delay))
except errors.FloodWaitError as e:
await asyncio.sleep(e.seconds)
except Exception:
continue
return joined
async def mass_react(client: TelegramClient, channel, post_ids: list,
reaction='👍', per_account: int = 100, delay: tuple = (5, 15)):
"""Массовые реакции на посты"""
for pid in post_ids[:per_account]:
try:
await client.send_reaction(channel, pid, reaction)
await asyncio.sleep(random.uniform(*delay))
except errors.FloodWaitError as e:
await asyncio.sleep(e.seconds)
except Exception:
continue
# ═══════════════════════════════════════════════════════════════
# МОДУЛЬ: Чекер юзернеймов
# ═══════════════════════════════════════════════════════════════
async def check_usernames(client: TelegramClient, usernames: list) -> dict:
"""Проверка доступности юзернеймов"""
results = {'free': [], 'taken': [], 'errors': []}
for username in usernames:
try:
entity = await client.get_entity(username)
results['taken'].append(username)
except errors.UsernameNotOccupiedError:
results['free'].append(username)
except Exception:
results['errors'].append(username)
await asyncio.sleep(0.5)
return results
# ═══════════════════════════════════════════════════════════════
# МОДУЛЬ: Масслукинг (просмотр Stories)
# ═══════════════════════════════════════════════════════════════
async def mass_story_view(client: TelegramClient, targets: list,
per_account: int = 200, delay: tuple = (3, 10)):
"""Массовый просмотр Telegram Stories"""
viewed = 0
for target in targets:
if viewed >= per_account:
break
try:
entity = await client.get_input_entity(target)
stories = await client(functions.stories.GetPeerStoriesRequest(
peer=entity))
if stories.stories:
await client(functions.stories.ReadStoriesRequest(
peer=entity,
max_id=max(s.id for s in stories.stories.stories)
))
viewed += 1
await asyncio.sleep(random.uniform(*delay))
except Exception:
continue
return viewed
# ═══════════════════════════════════════════════════════════════
# МОДУЛЬ: Прогрев аккаунтов
# ═══════════════════════════════════════════════════════════════
async def warmup_account(client: TelegramClient, days: int = 7,
daily_joins: int = 5, daily_messages: int = 3, daily_reactions: int = 10):
"""Прогрев аккаунта перед работой"""
for day in range(days):
for _ in range(random.randint(1, daily_joins)):
try:
dialogs = await client.get_dialogs()
groups = [d for d in dialogs if d.is_group]
if groups:
await client(JoinChannelRequest(random.choice(groups)))
except:
pass
await asyncio.sleep(random.uniform(300, 900))
for _ in range(random.randint(1, daily_messages)):
try:
await client.send_message('me', f'Warmup message {time.time()}')
except:
pass
await asyncio.sleep(random.uniform(600, 1800))
return True
# ═══════════════════════════════════════════════════════════════
# МОДУЛЬ: Постинг по каналам
# ═══════════════════════════════════════════════════════════════
async def channel_posting(client: TelegramClient, channels: list,
posts: list[dict], schedule: bool = False):
"""Публикация постов в каналы через бота-админа"""
for channel in channels:
for post in posts:
try:
entity = await client.get_entity(channel)
if post.get('photo'):
await client.send_file(entity, post['photo'],
caption=post.get('caption', ''))
else:
await client.send_message(entity, post.get('text', ''))
except Exception:
continue
await asyncio.sleep(random.uniform(10, 30))
# ═══════════════════════════════════════════════════════════════
# МОДУЛЬ: Снятие спамблока
# ═══════════════════════════════════════════════════════════════
async def remove_spamblock(client: TelegramClient) -> bool:
"""Снятие ограничений через SpamBot"""
try:
await client.send_message('SpamBot', '/start')
await asyncio.sleep(2)
msgs = await client.get_messages('SpamBot', limit=5)
for msg in msgs:
if msg.reply_markup and hasattr(msg.reply_markup, 'rows'):
for row in msg.reply_markup.rows:
for btn in row.buttons:
if 'ограничен' in (btn.text or '').lower() or \
'это я' in (btn.text or '').lower() or \
'продолжить' in (btn.text or '').lower():
await btn.click()
return True
except Exception:
pass
return False
# ═══════════════════════════════════════════════════════════════
# МОДУЛЬ: Диспетчер задач (оркестратор)
# ═══════════════════════════════════════════════════════════════
class TaskDispatcher:
"""Центральный диспетчер задач"""
def __init__(self):
self.tasks = {}
self.running = set()
self.results = {}
async def run_task(self, task_id: str, coro, accounts: list,
proxy_rotate=None):
"""Запуск задачи на пуле аккаунтов с ротацией"""
self.running.add(task_id)
results = []
for acc in accounts:
client = await self._get_client(acc, proxy_rotate)
try:
result = await coro(client)
results.append({'account': acc, 'result': result})
except Exception as e:
results.append({'account': acc, 'error': str(e)})
finally:
await client.disconnect()
self.running.discard(task_id)
self.results[task_id] = results
return results
async def _get_client(self, account, proxy):
"""Создать клиент с прокси"""
session = account.get('session')
api_id = account.get('api_id', 2040)
api_hash = account.get('api_hash', 'b18441a1ff607e10a989891a5462e627')
client = TelegramClient(
StringSession(session) if session else StringSession(),
api_id, api_hash,
proxy=proxy
)
await client.connect()
if not await client.is_user_authorized():
await client.start(phone=account['phone'])
return client
# ═══════════════════════════════════════════════════════════════
# МОДУЛЬ: Прокси-менеджер
# ═══════════════════════════════════════════════════════════════
async def proxy_check(proxy: dict) -> dict:
"""Проверка прокси перед назначением на аккаунт"""
import socket
try:
proto, host, port = proxy['type'], proxy['host'], proxy['port']
sock = socket.create_connection((host, port), timeout=5)
sock.close()
return {'proxy': proxy, 'alive': True, 'latency': None}
except Exception as e:
return {'proxy': proxy, 'alive': False, 'error': str(e)}
async def proxy_rotate(proxies: list, accounts: list) -> dict:
"""Назначение прокси на аккаунты (round-robin)"""
alive = [p for p in proxies if (await proxy_check(p))['alive']]
mapping = {}
for i, acc in enumerate(accounts):
mapping[acc['id']] = alive[i % len(alive)] if alive else None
return mapping