import asyncio
import os
import logging
import random
from datetime import datetime
from playwright.async_api import async_playwright
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s', handlers=[logging.FileHandler("logs/contracts.log"), logging.StreamHandler()])
logger = logging.getLogger("GIS-Ultimate")
USER_DATA_DIR = "/app/browser_context"
OBSIDIAN_BASE = "/app/obsidian/Gemini/Projects/GIS_ZHKH"
NAS_DOWNLOADS = "/app/downloads/nas/gis_archive"
BLACKLIST = [
"Справка по системе", "Регламенты и инструкции", "Часто задаваемые вопросы",
"Карта сайта", "Версия для слабовидящих", "Обращение в службу поддержки",
"Личный кабинет", "Главная страница", "Помещения (жилые дома)",
"Информационные сообщения", "Обращения и ответы на них", "Подключенные ЛС",
"Оплата ЖКУ", "Аналитика и отчеты", "Форум", "Свернуть поиск", "Найти",
"Очистить", "предыдущая", "следующая", "Выводить по"
]
async def send_tg_report(message):
token = os.getenv("TELEGRAM_BOT_TOKEN")
chat_id = os.getenv("TELEGRAM_CHAT_ID")
if not token or not chat_id: return
import aiohttp
url = f"https://api.telegram.org/bot{token}/sendMessage"
try:
async with aiohttp.ClientSession() as session:
await session.post(url, json={"chat_id": chat_id, "text": message, "parse_mode": "HTML"})
except: pass
async def human_delay(min_sec=4, max_sec=8):
await asyncio.sleep(random.uniform(min_sec, max_sec))
async def take_screenshot(page, title, subfolder):
safe_name = "".join([c for c in title if c.isalnum() or c in (' ', '_', '-')]).strip().replace(" ", "_")
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
filename = f"{safe_name}_{timestamp}.png"
path = f"{OBSIDIAN_BASE}/{subfolder}/{filename}"
os.makedirs(os.path.dirname(path), exist_ok=True)
await page.screenshot(path=path, full_page=True)
return filename
async def download_all_files(page, save_folder):
files_saved = []
dest_dir = f"{NAS_DOWNLOADS}/{save_folder}"
os.makedirs(dest_dir, exist_ok=True)
selectors = ["a[title*='Скачать']", "button:has-text('Скачать')", "a:has-text('Скачать все')", "a[href*='.pdf']", "a[href*='.zip']", "a[href*='.doc']", "a[href*='.rar']"]
for f in [page] + page.frames:
for selector in selectors:
try:
elements = await f.locator(selector).all()
for el in elements:
if not await el.is_visible(): continue
try:
async with page.expect_download(timeout=15000) as di:
await el.click()
download = await di.value
fname = download.suggested_filename
dest = f"{dest_dir}/{fname}"
await download.save_as(dest)
files_saved.append(fname)
except: continue
except: continue
return files_saved
async def phase_0_login(page):
await send_tg_report("🔍 Инициализация. Проверка сессии.")
await page.goto("https://dom.gosuslugi.ru/")
await human_delay()
for _ in range(30):
content = ""
for f in [page] + page.frames:
try: content += await f.content()
except: pass
if "Выйти" in content or "Александрович" in content:
await send_tg_report("✅ Сессия активна.")
return True
await asyncio.sleep(5)
return False
async def process_generic_list(page, url, section_name, folder_name, link_prefix=""):
await send_tg_report(f"📂 Захожу в раздел: {section_name}")
await page.goto(url)
await human_delay(5, 8)
processed = set()
page_num = 1
while True:
await send_tg_report(f"📄 Сканирую страницу {page_num}...")
await take_screenshot(page, f"Список_стр_{page_num}", f"{folder_name}/lists")
while True:
targets = []
for f in [page] + page.frames:
try:
elements = await f.locator("a").all()
for el in elements:
if await el.is_visible():
txt = (await el.inner_text()).strip()
if len(txt) > 10 and txt not in processed:
is_blacklisted = any(b in txt for b in BLACKLIST)
if not is_blacklisted:
if not link_prefix or txt.startswith(link_prefix):
targets.append((el, txt))
except: pass
if targets: break
if not targets: break # Все ссылки на этой странице обработаны
el, txt = targets[0]
safe_name = "".join([c for c in txt if c.isalnum() or c in (' ', '_', '-')]).strip().replace(" ", "_")
processed.add(txt)
await send_tg_report(f"⏳ Открываю: {txt[:40]}...")
try:
await el.click()
await human_delay(4, 7)
inner_scr = await take_screenshot(page, safe_name, f"{folder_name}/details")
files = await download_all_files(page, f"{folder_name}/{safe_name}")
path = f"{OBSIDIAN_BASE}/{folder_name}/{safe_name}.md"
os.makedirs(os.path.dirname(path), exist_ok=True)
with open(path, "w", encoding="utf-8") as f_out:
f_out.write(f"# {txt}\n\n![[Gemini/Projects/GIS_ZHKH/{folder_name}/details/{inner_scr}]]\n\n## Файлы\n")
if files:
for fn in files: f_out.write(f"- `{fn}`\n")
else:
f_out.write("Файлов нет.\n")
await send_tg_report(f"✅ Готово. Файлов: {len(files)}")
await page.go_back()
await human_delay(4, 7)
except Exception as e:
logger.error(f"Error on {txt}: {e}")
await page.goto(url)
await human_delay(5, 8)
# Пагинация
next_found = False
for f in [page] + page.frames:
try:
btn = f.locator("a:has-text('следующая')").first
if await btn.is_visible():
await btn.click()
page_num += 1
next_found = True
await human_delay(5, 8)
break
except: pass
if not next_found: break
await send_tg_report(f"🏆 Раздел {section_name} полностью выкачан!")
async def phase_3_contracts(page):
await send_tg_report("📂 Захожу в раздел: Договора")
await page.goto("https://dom.gosuslugi.ru/#!/house-info")
await human_delay(4, 6)
# Клик по иконке раздела
for f in [page] + page.frames:
try:
btn = f.locator("text='Информация о договорах по дому'").first
if await btn.is_visible():
await btn.click()
await human_delay(4, 6)
break
except: pass
# Клик Найти
for f in [page] + page.frames:
try:
btn = f.locator("button:has-text('Найти')").first
if await btn.is_visible():
await btn.click()
await human_delay(6, 10)
break
except: pass
processed = set()
page_num = 1
while True:
await send_tg_report(f"📄 Договора: Страница {page_num}...")
await take_screenshot(page, f"Договора_Стр_{page_num}", "contracts/lists")
while True:
targets = []
for f in [page] + page.frames:
try:
elements = await f.locator("a").all()
for el in elements:
if await el.is_visible():
txt = (await el.inner_text()).strip()
if txt.startswith("Договор") and "Закон" not in txt and txt not in processed and len(txt) > 15:
targets.append((el, txt))
except: pass
if targets: break
if not targets: break
el, txt = targets[0]
safe_name = "".join([c for c in txt if c.isalnum() or c in (' ', '_', '-')]).strip().replace(" ", "_")
processed.add(txt)
await send_tg_report(f"⏳ Открываю договор: {txt[:40]}...")
try:
await el.click()
await human_delay(5, 8)
inner_scr = await take_screenshot(page, safe_name, "contracts/details")
files = await download_all_files(page, f"contracts/{safe_name}")
path = f"{OBSIDIAN_BASE}/contracts/{safe_name}.md"
os.makedirs(os.path.dirname(path), exist_ok=True)
with open(path, "w", encoding="utf-8") as f_out:
f_out.write(f"# {txt}\n\n![[Gemini/Projects/GIS_ZHKH/contracts/details/{inner_scr}]]\n\n## Файлы\n")
if files:
for fn in files: f_out.write(f"- `{fn}`\n")
else:
f_out.write("Файлов нет.\n")
await send_tg_report(f"✅ Готово. Файлов: {len(files)}")
# Клик по хлебным крошкам для возврата
back_clicked = False
for f in [page] + page.frames:
try:
bbtn = f.locator("text='Информация о договорах по дому'").first
if await bbtn.is_visible(timeout=1000):
await bbtn.click()
back_clicked = True
break
except: pass
if not back_clicked:
await page.go_back()
await human_delay(5, 8)
except Exception as e:
logger.error(f"Error on {txt}: {e}")
await page.go_back()
await human_delay(5, 8)
# Пагинация
next_found = False
for f in [page] + page.frames:
try:
btn = f.locator("a:has-text('следующая')").first
if await btn.is_visible():
await btn.click()
page_num += 1
next_found = True
await human_delay(5, 8)
break
except: pass
if not next_found: break
await send_tg_report("🏆 Раздел Договора полностью выкачан!")
async def run_ultimate_mission():
async with async_playwright() as p:
context = await p.chromium.launch_persistent_context(
user_data_dir=USER_DATA_DIR, headless=False, args=["--no-sandbox", "--disable-setuid-sandbox", "--start-maximized"]
)
page = context.pages[0] if context.pages else await context.new_page()
await page.set_viewport_size({"width": 1600, "height": 1024})
await send_tg_report("🚀 ФИНАЛЬНЫЙ АГЕНТ ЗАПУЩЕН. Жду сессию.")
if await phase_0_login(page):
# 1. Госпочта (Уведомления)
await process_generic_list(page, "https://my.dom.gosuslugi.ru/citizen-cabinet/#!/hcsi/nnl?tab=INCOMING", "Госпочта (Уведомления)", "notifications")
# 2. Обращения
await process_generic_list(page, "https://my.dom.gosuslugi.ru/citizen-cabinet/#!/appeals", "Обращения", "appeals")
# 3. Договоры
await phase_3_contracts(page)
await send_tg_report("🎉 ВСЕ ЗАДАЧИ ВЫПОЛНЕНЫ! СВОБОДА!")
while True: await asyncio.sleep(3600)
if __name__ == "__main__":
asyncio.run(run_ultimate_mission())