Загрузка данных
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 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36",
"Accept": "*/*",
"Accept-Language": "ru-RU,ru;q=0.9,en-US;q=0.8,en;q=0.7",
"Origin": "https://vk.com",
"Referer": "https://vk.com/dev/execute",
"X-Requested-With": "XMLHttpRequest"
}
class DevPortalCleaner:
def __init__(self):
self.session = requests.Session()
self.session.headers.update(HEADERS)
self.dev_hash = None
self.init_cookies()
def init_cookies(self):
for c in COOKIES_DATA:
val = c["value"]
# Устанавливаем куки на все домены, включая vk.com для портала разработчиков
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 auth_dev_portal(self) -> bool:
print("[*] Подключение к официальной API-консоли ВКонтакте (vk.com/dev)...")
try:
resp = self.session.get("https://vk.com/dev/execute", timeout=15)
html = resp.text
if "login" in resp.url or "act=blocked" in resp.url:
print("[!] Ошибка: Сессия недействительна для vk.com.")
return False
# Извлекаем хэш для выполнения API-методов
match = re.search(r"Dev\.methodRun\s*\([^,]+,\s*['\"]([^'\"]+)['\"]\)", html)
if not match:
match = re.search(r"Dev\.runMethod\s*\([^,]+,\s*['\"]([^'\"]+)['\"]\)", html)
if not match:
match = re.search(r'data-hash=["\']([a-zA-Z0-9_\-]+)["\']', html)
if match:
self.dev_hash = match.group(1)
print(f"[+] Ключ API-консоли успешно получен: {self.dev_hash}")
return True
else:
print("[!] Ошибка: Не удалось найти системный ключ API на странице разработчика.")
return False
except Exception as e:
print(f"[!] Сетевая ошибка при входе в консоль: {e}")
return False
def delete_friend_api(self, target_id: int) -> tuple:
"""
Отправляет честный API запрос friends.delete через консоль разработчика.
Возвращает (успех: bool, ответ: str)
"""
api_code = f'return API.friends.delete({{"user_id": {target_id}}});'
payload = {
"act": "a_run_method",
"al": "1",
"hash": self.dev_hash,
"method": "execute",
"param_code": api_code,
"param_v": "5.131"
}
try:
resp = self.session.post("https://vk.com/dev", data=payload, timeout=15)
text = resp.text.strip()
# Очистка системного мусора AJAX-ответа ВК
if text.startswith("<!--"):
text = text[4:]
if text.endswith("-->"):
text = text[:-3]
# Ищем чистый JSON ответ от API
match = re.search(r'(\{"response":.*?\})', text, re.DOTALL)
if not match:
match = re.search(r'(\{"error":.*?\})', text, re.DOTALL)
if match:
json_str = match.group(1)
res_obj = json.loads(json_str)
# Обработка успешного ответа
if "response" in res_obj:
r = res_obj["response"]
if isinstance(r, dict):
if r.get("success") == 1 or r.get("friend_deleted") == 1:
return True, "API Success: 1 (Реально удален)"
elif r.get("out_request_deleted") == 1:
return True, "Отменена исходящая заявка"
elif isinstance(r, int) and r == 1:
return True, "API Success: 1"
return True, f"Успешно (Сырой ответ API: {r})"
# Обработка API ошибок (например, капчи)
if "error" in res_obj:
err = res_obj["error"]
err_code = err.get("error_code")
err_msg = err.get("error_msg", "")
if err_code == 14:
return False, "КАПЧА! ВКонтакте временно заблокировал частые действия."
elif err_code == 29:
return False, "Достигнут лимит API ВКонтакте."
return False, f"Ошибка API [{err_code}]: {err_msg}"
# Если ВК вернул HTML с ошибкой, а не JSON
return False, f"Не удалось прочитать ответ сервера: {text[:60]}"
except Exception as e:
return False, f"Ошибка соединения: {e}"
def main():
print("=" * 60)
print(" НАСТОЯЩЕЕ API-УДАЛЕНИЕ ДРУЗЕЙ (DEV CONSOLE)")
print("=" * 60)
cleaner = DevPortalCleaner()
if not cleaner.auth_dev_portal():
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"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[*] Контрольный тест API на пользователе: {test_name} (id{test_id})...")
ok, response_text = cleaner.delete_friend_api(test_id)
print(f" Ответ API ВКонтакте: {response_text}")
if not ok:
print("\n" + "!" * 60)
print("[!] ВНИМАНИЕ: API отклонил запрос на удаление!")
print(" Процесс остановлен, чтобы не работать вхолостую.")
print("!" * 60)
sys.exit(1)
print(f"\n[+] ТЕСТ ПРОЙДЕН! Сервер ВКонтакте подтвердил удаление (success: 1).")
confirm = input(f"\nПродолжить удаление остальных {len(targets) - 1} пользователей? (yes/no): ").strip().lower()
if confirm != "yes":
print("[-] Процесс остановлен пользователем.")
sys.exit(0)
print("\n[*] Запуск массового удаления через API...")
deleted_count = 1
for fid, name in targets[1:]:
ok, resp_str = cleaner.delete_friend_api(fid)
if ok:
deleted_count += 1
print(f"[{deleted_count}/{len(targets)}] Удален: {name} (id{fid})")
else:
print(f"[x] Ошибка: {name} (id{fid}) -> {resp_str}")
# Останавливаем процесс, если выскочила капча
if "КАПЧА" in resp_str or "лимит" in resp_str.lower():
print("\n[!] Обнаружена блокировка от ВК. Остановка скрипта для безопасности.")
break
time.sleep(1.3)
print(f"\n[+] Процесс завершен! Успешно отработано: {deleted_count} из {len(targets)}.")
if __name__ == "__main__":
main()
EOF