Загрузка данных


cat << 'EOF' > clean_friends.py
import sys
import time
import json
import re
import os
import requests

COOKIES_DATA = [
    {"name": "remixuacck", "value": "85dca311dce9d73521", "domain": ".vk.ru", "path": "/"},
    {"name": "remixdark_color_scheme", "value": "1", "domain": ".vk.ru", "path": "/"},
    {"name": "httoken", "value": "Cgte3N8Ypeja1DYG4vPefa2lr3XZDdzjDaFjDbXMHJXVfWUslZCpVYJv55EFLnV9PfIyoxT0usuGAshtshZWkxiE60e4cbtLSdS4IsDlj-pRGtqwakjyd8GxsJI6-X1dZTU", "domain": ".m.vk.ru", "path": "/"},
    {"name": "remixlang", "value": "0", "domain": ".vk.ru", "path": "/"},
    {"name": "remixff", "value": "10111111111101", "domain": ".vk.ru", "path": "/"},
    {"name": "remixstid", "value": "960128973_9LrUKD1VN6V9WuhzlVtB8KJD2S2pzXGDoIHpShgLIsH", "domain": ".vk.ru", "path": "/"},
    {"name": "remixua", "value": "17%7C621%7C86%7C677989468", "domain": ".vk.ru", "path": "/"},
    {"name": "remixdt", "value": "7200", "domain": ".vk.ru", "path": "/"},
    {"name": "remixmdevice", "value": "468/1037/2.3076923076923075/!!!!!!!!!!!/468", "domain": ".vk.ru", "path": "/"},
    {"name": "remixstlid", "value": "9088231374865086373_c7v7GTeuJsPheSw8dTr3KEjZauNZCcWfuNw37uYtLbc", "domain": ".vk.ru", "path": "/"},
    {"name": "remixcolor_scheme_mode", "value": "auto", "domain": ".vk.ru", "path": "/"},
    {"name": "remixsuc", "value": "1%3A", "domain": ".vk.ru", "path": "/"},
    {"name": "remixrefkey", "value": "46b62ba5d1b4d27b3a", "domain": ".vk.ru", "path": "/"},
    {"name": "remixmsts", "value": "%7B%22data%22%3A%5B%5B1791534625%2C%22web_stats_request_error%22%2C1%2C%22https%3A//stats.vk-portal.net/web-stats/p%22%2Ctrue%2C0%5D%2C%5B1791534626%2C%22entries_requests%22%2C1%2C%22audio%22%2C%22mvk%22%5D%2C%5B1791534626%2C%22entries_requests%22%2C1%2C%22audio_player_bottom%22%2C%22mvk%22%5D%2C%5B1791534633%2C%22counters_check%22%2C1%5D%2C%5B1791534880%2C%22web_stats_request_error%22%2C1%2C%22https%3A//m.vk.ru/usefull.php%22%2Ctrue%2C0%5D%2C%5B1791534880%2C%22web_stats_request_error%22%2C1%2C%22https%3A//m.vk.ru/special.php%22%2Ctrue%2C0%5D%5D%2C%22uniqueId%22%3A356816131.87341696%7D", "domain": ".vk.ru", "path": "/"},
    {"name": "remixnsid", "value": "vk1.a.atCUp48xWiA2T-BTfSwb4LuTyUE38ZY_7ZSGDybbT8EwbSInkmjtcuzAYNuhPw1gdxVKwjE-RSnWR3-jhvs5phqpC2pGpPCDHKAbUOVFxV71lKwZkUBr27eeG5buqSvhuag8Ae28M-NDIvAnp0j3GjfTVVevATCqCIdmtFy3Gts0KgGCGyHfVkIH7UHxcwKG", "domain": "m.vk.ru", "path": "/"},
    {"name": "httoken", "value": "cPLne8Hv_rs7lMMlhSk5Nush2BP11DugSoU5Q5xxq6DGRJ20IJWDjl8EZY1oDOD7LNOYJNBHUirzMHgHvsTvnRzxgwcacp2i7VdVlUoRBVBsM_DB1aQx7Rq-vPNHo3gy-0E", "domain": ".vk.ru", "path": "/"},
    {"name": "remixcurr_audio", "value": "undefined", "domain": ".vk.ru", "path": "/"},
    {"name": "remixdmgr", "value": "e25cacfa8eaeb542a4121715292ac9dd063768a26d15aebc7cb1cfe13c66d5ae", "domain": ".vk.ru", "path": "/"},
    {"name": "remixdmgr_tmp", "value": "98790d44713e491054b0fc82cdca08eb8b0c74be713a266b4508dc0f31bcc204", "domain": ".vk.ru", "path": "/"},
    {"name": "remixgp", "value": "7e2ccff978f2c6f8eb16d61aa8712618", "domain": ".vk.ru", "path": "/"},
    {"name": "remixmdv", "value": "EdaHXljyjilXaPud", "domain": ".vk.ru", "path": "/"},
    {"name": "remixmvk-fp", "value": "6b40bf508d27d26aa011c5920d4b2951", "domain": ".vk.ru", "path": "/"},
    {"name": "remixsid", "value": "1_IkQyQAe1Ys2aGq_Ej0UbN6tuG1P8GnY4gdWIT6EQxFS40xK4Z0e1FF-JJtSVWP2XbhqYkhgHedtxVUZFl1GWjg", "domain": ".vk.ru", "path": "/"},
    {"name": "remixuas", "value": "M2QzYjI4YjlkYmFjOTdmZWU1NjdkNWIy", "domain": ".vk.ru", "path": "/"}
]

MOBILE_HEADERS = {
    "User-Agent": "Mozilla/5.0 (Linux; Android 14; SM-S928B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Mobile Safari/537.36",
    "Accept": "*/*",
    "Accept-Language": "ru-RU,ru;q=0.9,en-US;q=0.8,en;q=0.7",
    "Origin": "https://m.vk.ru",
    "Referer": "https://m.vk.ru/friends",
    "X-Requested-With": "XMLHttpRequest"
}


class TruePostCleaner:
    def __init__(self):
        self.session = requests.Session()
        self.session.headers.update(MOBILE_HEADERS)
        self.user_id = 688006907
        self.action_hash = "3726c060ba1f5ee010"
        self.init_cookies()

    def init_cookies(self):
        for c in COOKIES_DATA:
            val = c["value"]
            for dom in [".vk.ru", "vk.ru", ".m.vk.ru", "m.vk.ru", ".vk.com", "vk.com"]:
                self.session.cookies.set(c["name"], val, domain=dom, path="/")

    def check_connection(self) -> bool:
        print("[*] Проверка сессии ВКонтакте...")
        try:
            resp = self.session.get("https://m.vk.ru/feed", timeout=15)
            if "login" in resp.url or "act=blocked" in resp.url:
                print("[!] Ошибка: сессия отклонена сайтом.")
                return False
            print(f"[+] Сессия активна! Твой ID: {self.user_id}")
            return True
        except Exception as e:
            print(f"[!] Ошибка подключения: {e}")
            return False

    def fetch_current_friend_ids(self) -> set:
        """
        Запрашивает актуальный список ID друзей для контрольной проверки
        """
        current_ids = set()
        try:
            resp = self.session.get("https://m.vk.ru/friends?section=all", timeout=15)
            html = resp.text
            raw_ids = re.findall(r'\[\s*(\d+)\s*,\s*["\']', html)
            if not raw_ids:
                raw_ids = re.findall(r'/id(\d+)', html)
            for s in raw_ids:
                fid = int(s)
                if 0 < fid < 2000000000 and fid != self.user_id:
                    current_ids.add(fid)
        except Exception:
            pass
        return current_ids

    def send_real_post_delete(self, target_id: int) -> tuple:
        """
        Отправляет честный POST-запрос на удаление через шлюз мобильной версии
        """
        url = "https://m.vk.ru/friends"
        payload = {
            "act": "remove",
            "id": target_id,
            "_hash": self.action_hash,
            "_ajax": "1"
        }
        headers = dict(MOBILE_HEADERS)
        headers["Referer"] = f"https://m.vk.ru/id{target_id}"

        try:
            resp = self.session.post(url, data=payload, headers=headers, timeout=15)
            raw_text = resp.text.strip()
            
            # Проверяем, вернулся ли ответ ВК с кодом успеха или редиректом
            if resp.status_code == 200:
                if "error" in raw_text.lower():
                    return False, f"Ошибка ВК: {raw_text[:70]}"
                return True, raw_text[:80]
            else:
                return False, f"HTTP Статус {resp.status_code}"
        except Exception as e:
            return False, f"Сетевая ошибка: {e}"


def main():
    print("=" * 60)
    print("      НАСТОЯЩЕЕ POST-УДАЛЕНИЕ ДРУЗЕЙ ВКОНТАКТЕ")
    print("=" * 60)

    cleaner = TruePostCleaner()
    if not cleaner.check_connection():
        sys.exit(1)

    backup_file = "deleted_friends_backup.txt"
    if not os.path.exists(backup_file):
        print(f"[!] Файл {backup_file} не найден!")
        sys.exit(1)

    targets = []
    with open(backup_file, "r", encoding="utf-8") as f:
        for line in f:
            match = re.search(r"vk\.(?:com|ru)/id(\d+)\s*-\s*(.*)", line.strip())
            if match:
                targets.append((int(match.group(1)), match.group(2).strip()))

    print(f"[+] Загружено {len(targets)} пользователей из резервного списка.")
    if not targets:
        print("[!] Список пуст.")
        sys.exit(0)

    # --- ТЕСТ НА 1-М ПОЛЬЗОВАТЕЛЕ С РЕАЛЬНОЙ ПРОВЕРКОЙ СПИСКА ---
    test_id, test_name = targets[0]
    print(f"\n[*] Контрольный тест POST-запроса на пользователе:")
    print(f"    Имя: {test_name} (id{test_id})")
    print(f"    Отправка POST act=remove с хэшем {cleaner.action_hash}...")

    ok, response_preview = cleaner.send_real_post_delete(test_id)
    print(f"    Сырой ответ сервера: {response_preview}")

    print("[*] Проверка: остался ли id{0} в списке друзей...".format(test_id))
    time.sleep(1.5)
    friends_now = cleaner.fetch_current_friend_ids()

    if test_id in friends_now:
        print(f"\n[!] ВНИМАНИЕ: Пользователь id{test_id} ВСЁ ЕЩЁ В СПИСКЕ ДРУЗЕЙ!")
        print("    Запрос не применился на сервере. Процесс остановлен.")
        sys.exit(1)

    print(f"\n[+] ПОДТВЕРЖДЕНО! Пользователь id{test_id} ИСЧЕЗ из списка друзей.")
    print("    POST-запрос реально сработал на серверах ВКонтакте!")

    confirm = input(f"\nПродолжить удаление остальных {len(targets) - 1} пользователей? (yes/no): ").strip().lower()
    if confirm != "yes":
        print("[-] Процесс остановлен пользователем.")
        sys.exit(0)

    print("\n[*] Запуск массового удаления...")
    deleted_count = 1  # Первый уже удален

    for fid, name in targets[1:]:
        ok, resp_str = cleaner.send_real_post_delete(fid)

        if ok:
            deleted_count += 1
            print(f"[{deleted_count}/{len(targets)}] Реально удален: {name} (id{fid})")
        else:
            print(f"[x] Ошибка: {name} (id{fid}) -> {resp_str}")

        time.sleep(1.3)

    print(f"\n[+] Процесс завершен! Всего удалено: {deleted_count} из {len(targets)}.")


if __name__ == "__main__":
    main()
EOF