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


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": "UPml6IBSupOdTjtR8mNZ7CDJH0Bb-N0esLGzqqiRx-VXuCx3cdQIwp4tWaJS0QIK1eTg2hEvQckCkMj-g6PVDbyyuB6rim7-ywQlzbVBKzDDZG9tixx4Fve5ABSGe4YKFX0", "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%5B1791560891%2C%22entries_requests%22%2C1%2C%22core_spa%22%2C%22mvk%22%5D%2C%5B1791560891%2C%22entries_requests%22%2C1%2C%22audio%22%2C%22mvk%22%5D%2C%5B1791560891%2C%22entries_requests%22%2C1%2C%22audio_player_bottom%22%2C%22mvk%22%5D%2C%5B1791560892%2C%22counters_check%22%2C1%5D%2C%5B1791560893%2C%22counters_check%22%2C1%5D%5D%2C%22uniqueId%22%3A294709293.0240143%7D", "domain": ".vk.ru", "path": "/"},
    {"name": "remixnsid", "value": "vk1.a.q6PpoNhyuYE3JJqmSWsTYOjNJxeCyJDYsYfp0O8aQ4nR3S0EteEVgNcKePzlpaQmtVGNz7nsDaPTuGNcj5Mv5gf-N1lMQa9zYFJ6e_eKbIOWpI0gek2grh97jU-rynbk7wGWgyN0dApyzKyd_3Fs5PyRttZpWmvsbLAP42DIUcGSB5xyoCQiP7DzbCUootU7", "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_p_qDcaNIQbIQ0PUocdN0K9ZWuaHLofwBUSYNRFPu1MBN8XQXuAEeZk2vVKRa8BdXqOlX9gOWInY-yp-6b7RNBw", "domain": ".vk.ru", "path": "/"},
    {"name": "remixuas", "value": "M2QzYjI4YjlkYmFjOTdmZWU1NjdkNWIy", "domain": ".vk.ru", "path": "/"}
]

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": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8",
    "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/"
}


class VerifiedVkCleaner:
    def __init__(self):
        self.session = requests.Session()
        self.session.headers.update(HEADERS)
        self.user_id = 688006907
        self.working_method = None
        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(f"[!] Ошибка: сессия не принята сайтом. Итоговый URL: {resp.url}")
                return False

            uid_match = re.search(r'"id"\s*:\s*(\d+)', resp.text)
            if not uid_match:
                uid_match = re.search(r'/id(\d+)', resp.text)
            if uid_match and int(uid_match.group(1)) > 0:
                self.user_id = int(uid_match.group(1))

            print(f"[+] Сессия успешно подтверждена! Твой ID: {self.user_id}")
            return True
        except Exception as e:
            print(f"[!] Ошибка сетевого подключения: {e}")
            return False

    def get_profile_state(self, target_id: int):
        """
        Запрашивает страницу пользователя и возвращает:
        (is_friend: bool, remove_url: str, hash_val: str)
        """
        url = f"https://m.vk.ru/id{target_id}"
        try:
            resp = self.session.get(url, timeout=15)
            html = resp.text

            is_friend = False
            if "act=remove" in html or "friends_act" in html or "У вас в друзьях" in html:
                is_friend = True

            remove_url = None
            url_match = re.search(r'href=["\'](/friends\?act=remove[^"\']+)["\']', html)
            if url_match:
                remove_url = "https://m.vk.ru" + url_match.group(1).replace("&amp;", "&")

            hash_val = None
            if remove_url:
                h_match = re.search(r'[&?]_?hash=([a-zA-Z0-9_\-]+)', remove_url)
                if h_match:
                    hash_val = h_match.group(1)

            if not hash_val:
                h_match = re.search(r'data-hash=["\']([a-zA-Z0-9_\-]+)["\']', html)
                if h_match:
                    hash_val = h_match.group(1)

            return is_friend, remove_url, hash_val
        except Exception as e:
            print(f"[!] Ошибка загрузки профиля id{target_id}: {e}")
            return False, None, None

    def test_and_find_working_method(self, target_id: int, remove_url: str, hash_val: str) -> bool:
        """
        Тестирует различные варианты отправки запроса на 1-м пользователе
        и проверяет результат в профиле.
        """
        print(f"\n[*] Подбор рабочего метода удаления для пользователя id{target_id}...")

        # Вариант 1: Прямой переход по ссылке подтверждения с отправкой формы
        if remove_url:
            print(" -> Попытка 1: Запрос через страницу подтверждения m.vk.ru...")
            try:
                page_resp = self.session.get(remove_url, timeout=15)
                # Ищем форму подтверждения
                form_match = re.search(r'<form[^>]+action=["\']([^"\']+)["\'][^>]*>(.*?)</form>', page_resp.text, re.DOTALL)
                if form_match:
                    form_action = form_match.group(1)
                    form_body = form_match.group(2)
                    if not form_action.startswith("http"):
                        form_action = "https://m.vk.ru" + form_action

                    inputs = dict(re.findall(r'<input[^>]+name=["\']([^"\']+)["\'][^>]+value=["\']([^"\']*)["\']', form_body))
                    post_resp = self.session.post(form_action, data=inputs, timeout=15)
                    print(f"    Ответ формы: HTTP {post_resp.status_code}")
                else:
                    # Если формы нет, пробуем прямой POST с _ajax=1
                    ajax_resp = self.session.post(remove_url, data={"_ajax": "1"}, headers={"X-Requested-With": "XMLHttpRequest"}, timeout=15)
                    print(f"    Ответ AJAX: HTTP {ajax_resp.status_code}")
            except Exception as e:
                print(f"    Ошибка Попытки 1: {e}")

            time.sleep(1.5)
            is_friend, _, _ = self.get_profile_state(target_id)
            if not is_friend:
                print("[+] Вариант 1 СРАБОТАЛ! Пользователь удален.")
                self.working_method = "direct_url_post"
                return True

        # Вариант 2: Стандартный POST на /friends
        if hash_val:
            print(" -> Попытка 2: Стандартный POST act=remove на m.vk.ru/friends...")
            payload = {
                "act": "remove",
                "id": target_id,
                "_hash": hash_val,
                "_ajax": "1"
            }
            headers = dict(HEADERS)
            headers["X-Requested-With"] = "XMLHttpRequest"
            headers["Referer"] = f"https://m.vk.ru/id{target_id}"

            try:
                resp = self.session.post("https://m.vk.ru/friends", data=payload, headers=headers, timeout=15)
                print(f"    Ответ сервера: HTTP {resp.status_code} | {resp.text[:60]}")
            except Exception as e:
                print(f"    Ошибка Попытки 2: {e}")

            time.sleep(1.5)
            is_friend, _, _ = self.get_profile_state(target_id)
            if not is_friend:
                print("[+] Вариант 2 СРАБОТАЛ! Пользователь удален.")
                self.working_method = "post_friends_ajax"
                return True

        # Вариант 3: Десктопный шлюз vk.ru/al_friends.php
        print(" -> Попытка 3: Десктопный шлюз vk.ru/al_friends.php...")
        desk_payload = {
            "act": "remove",
            "al": 1,
            "mid": target_id
        }
        if hash_val:
            desk_payload["hash"] = hash_val

        desk_headers = {
            "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36",
            "X-Requested-With": "XMLHttpRequest",
            "Origin": "https://vk.ru",
            "Referer": "https://vk.ru/friends"
        }

        try:
            resp = self.session.post("https://vk.ru/al_friends.php", data=desk_payload, headers=desk_headers, timeout=15)
            print(f"    Ответ vk.ru: HTTP {resp.status_code} | {resp.text[:70]}")
        except Exception as e:
            print(f"    Ошибка Попытки 3: {e}")

        time.sleep(1.5)
        is_friend, _, _ = self.get_profile_state(target_id)
        if not is_friend:
            print("[+] Вариант 3 СРАБОТАЛ! Пользователь удален.")
            self.working_method = "desktop_al_friends"
            return True

        return False

    def delete_single(self, target_id: int, hash_val: str) -> bool:
        """
        Выполняет удаление найденным рабочим методом
        """
        if self.working_method == "post_friends_ajax":
            payload = {
                "act": "remove",
                "id": target_id,
                "_hash": hash_val,
                "_ajax": "1"
            }
            headers = dict(HEADERS)
            headers["X-Requested-With"] = "XMLHttpRequest"
            headers["Referer"] = f"https://m.vk.ru/id{target_id}"
            try:
                res = self.session.post("https://m.vk.ru/friends", data=payload, headers=headers, timeout=15)
                return res.status_code == 200
            except Exception:
                return False

        elif self.working_method == "direct_url_post":
            url = f"https://m.vk.ru/friends?act=remove&id={target_id}&_hash={hash_val}"
            try:
                res = self.session.post(url, data={"_ajax": "1"}, headers={"X-Requested-With": "XMLHttpRequest"}, timeout=15)
                return res.status_code == 200
            except Exception:
                return False

        elif self.working_method == "desktop_al_friends":
            desk_payload = {"act": "remove", "al": 1, "mid": target_id, "hash": hash_val}
            desk_headers = {
                "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36",
                "X-Requested-With": "XMLHttpRequest",
                "Origin": "https://vk.ru",
                "Referer": "https://vk.ru/friends"
            }
            try:
                res = self.session.post("https://vk.ru/al_friends.php", data=desk_payload, headers=desk_headers, timeout=15)
                return res.status_code == 200
            except Exception:
                return False

        return False


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

    cleaner = VerifiedVkCleaner()
    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[*] Проверка текущего статуса пользователя: {test_name} (id{test_id})...")
    is_friend, remove_url, hash_val = cleaner.get_profile_state(test_id)

    if not is_friend:
        print(f"[+] Пользователь {test_name} (id{test_id}) УЖЕ НЕ В ДРУЗЬЯХ! Берем следующего для проверки...")
        # Если первый уже не в друзьях, берем второго для теста
        test_id, test_name = targets[1]
        is_friend, remove_url, hash_val = cleaner.get_profile_state(test_id)

    print(f"    Статус в друзьях: {'ДА' if is_friend else 'НЕТ'}")
    print(f"    Ссылка на отписку: {remove_url}")
    print(f"    Хэш безопасности: {hash_val}")

    success = cleaner.test_and_find_working_method(test_id, remove_url, hash_val)

    if not success:
        print("\n" + "!" * 60)
        print("[!] ВНИМАНИЕ: Ни один метод не смог удалить тестового пользователя.")
        print("    ВКонтакте отклонил команды или требует ручного подтверждения.")
        print("!" * 60)
        sys.exit(1)

    print("\n" + "=" * 60)
    print(f"[+] ПРОВЕРКА ПРОЙДЕНА: Пользователь id{test_id} РЕАЛЬНО УДАЛЕН!")
    print(f"    Рабочий метод: {cleaner.working_method}")
    print("=" * 60)

    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 = cleaner.delete_single(fid, hash_val)
        if ok:
            deleted_count += 1
            print(f"[{deleted_count}/{len(targets)}] Удален: {name} (id{fid})")
        else:
            print(f"[x] Ошибка: {name} (id{fid})")

        time.sleep(1.3)

    print(f"\n[+] Процесс завершен! Успешно обработано: {deleted_count} из {len(targets)}.")


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