Zero Block
Click "Block Editor" to enter the edit mode. Use layers, shapes and customize adaptability. Everything is in your hands.
Tilda Publishing
create your own block from scratch
# --- НАСТРОЙКИ ---
VK_TOKEN = 'vk1.a.siOvb9OmT4WPRWOHfXC5olSr2GI3GGKTSXWeX0oAOD7b9_WNGtBDre1uZ2I1ImnXBUV0HnQYns_VHaQepSXhiGxRslTwioedTwe9qUlAtYsjdhHS4y7xqO_5Of4C6xxU79hx1qDHN9nPGRBO_zHekWWxb7gDCd0wo9fLa4K2idpfBfusz8B_O53HfGKaCtmtnF_IIbUF_eIEHbgz6vs2VA'

# ID групп для проверки (по одному на строке)
GROUP_IDS = """
216946255
229580483
216945220
225144088
225427887
225423241
196604934
51334734
225027346
225073535
"""

# Настройки для больших списков
SAVE_EVERY_N_GROUPS = 50         # Сохранять результаты каждые N групп
RATE_LIMIT_DELAY = 300           # Пауза при превышении лимита (5 минут)
GROUP_PROCESSING_DELAY = 3.0     # Пауза между группами (3 секунды)
RESUME_FROM_GROUP = 0            # Начать с группы номер N (0 = сначала)

# Дополнительные настройки для избежания лимитов
REQUEST_TIMEOUT = 60             # Таймаут запроса (60 секунд)
MAX_RETRIES = 5                  # Максимум попыток (увеличено)
RETRY_DELAY = 10                 # Пауза между повторами (10 секунд)

# --- КОНЕЦ НАСТРОЕК ---

import requests
import time
import re
import json
import os
from typing import List, Dict, Any
from datetime import datetime

class VKCommunitiesChecker:
    def __init__(self, access_token: str):
        """
        Инициализация проверщика сообществ ВК
        """
        self.access_token = access_token
        self.api_url = "https://api.vk.com/method/"
        self.api_version = "5.131"
        
        # Файлы для сохранения результатов
        self.hidden_members_file = "groups_with_hidden_members.txt"
        self.non_russian_names_file = "groups_with_non_russian_names.txt"
        self.progress_file = "progress.json"
        self.error_log_file = "error_log.txt"
        
        # Допустимые символы
        self.allowed_special_chars = set(['|', '/', '[', ']', ' ', '-', '_', '.', '!', '?', 
                                         '(', ')', '№', '@', '#', '$', '%', '^', '&', '*',
                                         '+', '=', '~', '`', ':', ';', '"', "'", '<', '>',
                                         ',', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'])
        
        # Счетчики и результаты
        self.processed_groups = 0
        self.hidden_members_groups = []
        self.non_russian_names_groups = []
        self.skipped_groups = []  # Группы, которые не удалось обработать
        
        # Статистика запросов
        self.total_requests = 0
        self.rate_limit_hits = 0
        
        # Загружаем прогресс если есть
        self.load_progress()
    
    def log_error(self, message: str):
        """Логирование ошибок в файл"""
        try:
            with open(self.error_log_file, 'a', encoding='utf-8') as f:
                timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
                f.write(f"[{timestamp}] {message}\n")
        except:
            pass
    
    def parse_group_ids(self, group_ids_string: str) -> List[int]:
        """Парсинг строки с ID групп"""
        group_ids = []
        for line in group_ids_string.strip().split('\n'):
            line = line.strip()
            if line and line.isdigit():
                group_ids.append(int(line))
        return group_ids
    
    def make_api_request(self, method: str, params: Dict[str, Any]) -> Dict[str, Any]:
        """Выполнение запроса к API ВК с улучшенной обработкой лимитов"""
        max_retries = MAX_RETRIES
        retry_count = 0
        
        while retry_count < max_retries:
            try:
                params['access_token'] = self.access_token
                params['v'] = self.api_version
                
                # Увеличенный таймаут
                response = requests.get(
                    f"{self.api_url}{method}", 
                    params=params, 
                    timeout=REQUEST_TIMEOUT
                )
                response.raise_for_status()
                data = response.json()
                
                self.total_requests += 1
                
                if 'error' in data:
                    error_code = data['error'].get('error_code', 0)
                    error_msg = data['error'].get('error_msg', 'Unknown error')
                    
                    # Обработка rate limit
                    if error_code == 29:  # Rate limit reached
                        self.rate_limit_hits += 1
                        print(f"   ⏳ Rate limit reached (попадание #{self.rate_limit_hits})")
                        print(f"   🕐 Ожидание {RATE_LIMIT_DELAY} секунд ({RATE_LIMIT_DELAY//60} мин)...")
                        
                        # Логируем в файл
                        self.log_error(f"Rate limit hit #{self.rate_limit_hits} on method {method}, group_id: {params.get('group_id', 'N/A')}")
                        
                        time.sleep(RATE_LIMIT_DELAY)
                        retry_count += 1
                        
                        # Дополнительная пауза после rate limit
                        if retry_count < max_retries:
                            print(f"   ⏳ Дополнительная пауза {RETRY_DELAY} сек перед повтором...")
                            time.sleep(RETRY_DELAY)
                        
                        continue
                    
                    # Другие ошибки API
                    self.log_error(f"API error {error_code}: {error_msg} on method {method}")
                    return {'error': {'code': error_code, 'msg': error_msg}}
                
                return data.get('response', {})
                
            except requests.Timeout:
                retry_count += 1
                error_msg = f"Timeout error (попытка {retry_count}/{max_retries})"
                print(f"   ⏰ {error_msg}")
                self.log_error(f"Timeout on method {method}: {error_msg}")
                
                if retry_count < max_retries:
                    time.sleep(RETRY_DELAY)
                else:
                    return {'timeout_error': error_msg}
            
            except requests.RequestException as e:
                retry_count += 1
                error_msg = f"Network error (попытка {retry_count}/{max_retries}): {e}"
                print(f"   🌐 {error_msg}")
                self.log_error(f"Network error on method {method}: {error_msg}")
                
                if retry_count < max_retries:
                    time.sleep(RETRY_DELAY)
                else:
                    return {'network_error': str(e)}
            
            except Exception as e:
                error_msg = f"Unexpected error: {e}"
                print(f"   ❌ {error_msg}")
                self.log_error(f"Unexpected error on method {method}: {error_msg}")
                return {'unexpected_error': str(e)}
        
        return {'max_retries_exceeded': f'Exceeded {max_retries} retries'}
    
    def safe_get_error_info(self, response_data: Any) -> tuple:
        """Безопасное получение информации об ошибке"""
        try:
            if isinstance(response_data, dict) and 'error' in response_data:
                error_info = response_data['error']
                if isinstance(error_info, dict):
                    code = error_info.get('code', 0)
                    msg = error_info.get('msg', '')
                    return code, msg
        except:
            pass
        return 0, ""
    
    def analyze_members_access(self, group_id: int) -> bool:
        """Анализ доступности участников"""
        print(f"   🔍 Проверяем доступ к участникам...")
        
        params = {
            'group_id': group_id,
            'count': 2,  # Уменьшаем количество для экономии лимитов
            'offset': 0,
            'fields': 'first_name'
        }
        
        response = self.make_api_request('groups.getMembers', params)
        
        # Обработка различных типов ошибок
        if 'error' in response:
            error_code, error_msg = self.safe_get_error_info(response)
            print(f"   ⚠️ Ошибка API: код {error_code} - {error_msg}")
            
            # Специфические ошибки скрытых участников
            if error_code == 15 and 'hide members' in error_msg.lower():
                print(f"   ✅ ОПРЕДЕЛЕНО: СКРЫТЫ (участники скрыты группой)")
                return True
            
            # Другие коды ошибок доступа
            if error_code in [7, 15, 18, 30]:
                print(f"   ✅ ОПРЕДЕЛЕНО: СКРЫТЫ (нет доступа)")
                return True
            
            # Rate limit и другие временные ошибки
            if error_code in [29, 6, 10]:
                print(f"   ⚠️ ОПРЕДЕЛЕНО: СКРЫТЫ (временная ошибка)")
                return True
        
        # Обработка технических ошибок
        error_types = ['network_error', 'timeout_error', 'unexpected_error', 'max_retries_exceeded']
        for error_type in error_types:
            if error_type in response:
                print(f"   ⚠️ ОПРЕДЕЛЕНО: СКРЫТЫ ({error_type}: {response[error_type]})")
                return True
        
        # Успешное получение участников
        if 'items' in response and isinstance(response['items'], list):
            items_count = len(response['items'])
            total_count = response.get('count', 0)
            print(f"   📊 Получено {items_count} из {total_count} участников")
            
            # Известные скрытые группы
            if group_id in [220647061]:
                print(f"   📋 Известная скрытая группа (вы админ, но для других скрыты)")
                return True
            
            print(f"   ✅ ОПРЕДЕЛЕНО: ОТКРЫТЫ (получен список участников)")
            return False
        
        print(f"   ⚠️ ОПРЕДЕЛЕНО: СКРЫТЫ (нет данных об участниках)")
        return True
    
    def has_non_russian_chars(self, text: str) -> bool:
        """Проверка наличия английских букв в тексте"""
        for char in text:
            if char.isalpha():
                if not (ord('а') <= ord(char.lower()) <= ord('я') or 
                        ord('А') <= ord(char.upper()) <= ord('Я') or 
                        char.lower() in ['ё', 'Ё']):
                    if char not in self.allowed_special_chars:
                        return True
        return False
    
    def get_group_info(self, group_id: int) -> Dict[str, Any]:
        """Получение информации о группе с защитой от ошибок"""
        print(f"   📋 Получаем информацию о группе...")
        
        params = {
            'group_ids': group_id,
            'fields': 'name,type,is_closed,members_count'
        }
        
        response = self.make_api_request('groups.getById', params)
        
        # Проверяем, что ответ корректный
        if isinstance(response, list) and len(response) > 0:
            return response[0]
        elif isinstance(response, dict):
            if 'error' in response:
                error_code, error_msg = self.safe_get_error_info(response)
                print(f"   ⚠️ Ошибка получения информации: {error_code} - {error_msg}")
                self.log_error(f"Failed to get info for group {group_id}: {error_code} - {error_msg}")
            else:
                # Возможно технические ошибки
                for error_type in ['network_error', 'timeout_error', 'unexpected_error', 'max_retries_exceeded']:
                    if error_type in response:
                        print(f"   ⚠️ Техническая ошибка: {response[error_type]}")
                        self.log_error(f"Technical error for group {group_id}: {error_type} - {response[error_type]}")
                        break
            return {}
        else:
            print(f"   ⚠️ Неожиданный формат ответа от API")
            self.log_error(f"Unexpected response format for group {group_id}: {type(response)}")
            return {}
    
    def save_progress(self):
        """Сохранение прогресса в файл"""
        progress_data = {
            'processed_groups': self.processed_groups,
            'hidden_members_groups': self.hidden_members_groups,
            'non_russian_names_groups': self.non_russian_names_groups,
            'skipped_groups': self.skipped_groups,
            'total_requests': self.total_requests,
            'rate_limit_hits': self.rate_limit_hits,
            'timestamp': time.time()
        }
        
        try:
            with open(self.progress_file, 'w', encoding='utf-8') as f:
                json.dump(progress_data, f, ensure_ascii=False, indent=2)
            print(f"   💾 Прогресс сохранен (обработано {self.processed_groups} групп, запросов: {self.total_requests}, лимитов: {self.rate_limit_hits})")
        except Exception as e:
            print(f"   ⚠️ Ошибка сохранения прогресса: {e}")
            self.log_error(f"Failed to save progress: {e}")
    
    def load_progress(self):
        """Загрузка прогресса из файла"""
        if os.path.exists(self.progress_file):
            try:
                with open(self.progress_file, 'r', encoding='utf-8') as f:
                    progress_data = json.load(f)
                
                self.processed_groups = progress_data.get('processed_groups', 0)
                self.hidden_members_groups = progress_data.get('hidden_members_groups', [])
                self.non_russian_names_groups = progress_data.get('non_russian_names_groups', [])
                self.skipped_groups = progress_data.get('skipped_groups', [])
                self.total_requests = progress_data.get('total_requests', 0)
                self.rate_limit_hits = progress_data.get('rate_limit_hits', 0)
                
                print(f"📂 Загружен прогресс:")
                print(f"   👥 Обработано групп: {self.processed_groups}")
                print(f"   🔒 Скрытые участники: {len(self.hidden_members_groups)}")
                print(f"   🔤 Английские названия: {len(self.non_russian_names_groups)}")
                print(f"   ⏭️ Пропущенные группы: {len(self.skipped_groups)}")
                print(f"   📡 Всего запросов: {self.total_requests}")
                print(f"   🚫 Превышений лимита: {self.rate_limit_hits}")
            except Exception as e:
                print(f"⚠️ Ошибка загрузки прогресса: {e}")
                self.log_error(f"Failed to load progress: {e}")
    
    def save_final_results(self):
        """Сохранение финальных результатов"""
        try:
            # Сохраняем группы со скрытыми участниками
            if self.hidden_members_groups:
                with open(self.hidden_members_file, 'w', encoding='utf-8') as f:
                    for group_id in self.hidden_members_groups:
                        f.write(f"{group_id}\n")
                print(f"📄 Группы со скрытыми участниками сохранены в '{self.hidden_members_file}' ({len(self.hidden_members_groups)} групп)")
            else:
                print("✅ Нет групп со скрытыми участниками")
            
            # Сохраняем группы с английскими символами
            if self.non_russian_names_groups:
                with open(self.non_russian_names_file, 'w', encoding='utf-8') as f:
                    for group_id in self.non_russian_names_groups:
                        f.write(f"{group_id}\n")
                print(f"📄 Группы с английскими символами сохранены в '{self.non_russian_names_file}' ({len(self.non_russian_names_groups)} групп)")
            else:
                print("✅ Нет групп с английскими символами в названии")
            
            # Сохраняем пропущенные группы
            if self.skipped_groups:
                with open("skipped_groups.txt", 'w', encoding='utf-8') as f:
                    for group_id in self.skipped_groups:
                        f.write(f"{group_id}\n")
                print(f"⚠️ Пропущенные группы сохранены в 'skipped_groups.txt' ({len(self.skipped_groups)} групп)")
            
            # Удаляем файл прогресса после успешного завершения
            if os.path.exists(self.progress_file):
                os.remove(self.progress_file)
                print("🗑️ Временный файл прогресса удален")
                
        except Exception as e:
            print(f"❌ Ошибка сохранения результатов: {e}")
            self.log_error(f"Failed to save final results: {e}")
    
    def check_communities(self, group_ids_string: str):
        """Основная функция проверки сообществ"""
        print("🔍 Начинаем проверку сообществ ВКонтакте...\n")
        
        groups = self.parse_group_ids(group_ids_string)
        
        if not groups:
            print("❌ Не удалось получить список групп. Проверьте GROUP_IDS.")
            return
        
        total_groups = len(groups)
        print(f"📋 Найдено {total_groups} групп для проверки")
        print(f"⏰ Настройки: пауза между группами {GROUP_PROCESSING_DELAY}с, при лимите {RATE_LIMIT_DELAY//60}мин")
        
        # Начинаем с указанной группы
        start_index = max(0, RESUME_FROM_GROUP)
        if start_index > 0:
            print(f"🔄 Продолжаем с группы номер {start_index + 1}")
        
        print()
        
        try:
            for index in range(start_index, total_groups):
                group_id = groups[index]
                current_num = index + 1
                
                print(f"📊 Проверяем группу {current_num}/{total_groups} (ID: {group_id})")
                
                try:
                    group_info = self.get_group_info(group_id)
                    
                    if not group_info:
                        print(f"   ❌ Не удалось получить информацию о группе {group_id}")
                        self.skipped_groups.append(group_id)
                        self.processed_groups += 1
                        time.sleep(GROUP_PROCESSING_DELAY)
                        continue
                    
                    group_name = group_info.get('name', f'Группа {group_id}')
                    group_type = group_info.get('type', 'unknown')
                    is_closed = group_info.get('is_closed', 0)
                    members_count = group_info.get('members_count', 0)
                    
                    print(f"   📝 Название: {group_name}")
                    print(f"   📊 Тип: {group_type}, Закрытость: {is_closed}, Участников: {members_count}")
                    
                    # Проверка 1: Скрытые участники
                    if self.analyze_members_access(group_id):
                        print(f"   🔒 РЕЗУЛЬТАТ: УЧАСТНИКИ СКРЫТЫ от обычных пользователей")
                        self.hidden_members_groups.append(group_id)
                    else:
                        print(f"   👥 РЕЗУЛЬТАТ: УЧАСТНИКИ ОТКРЫТЫ для всех")
                    
                    # Проверка 2: Английские символы
                    if self.has_non_russian_chars(group_name):
                        print(f"   🔤 Найдены английские буквы в названии")
                        self.non_russian_names_groups.append(group_id)
                    else:
                        print(f"   ✅ Название содержит только допустимые символы")
                    
                    self.processed_groups += 1
                    
                    # Промежуточное сохранение каждые N групп
                    if self.processed_groups % SAVE_EVERY_N_GROUPS == 0:
                        self.save_progress()
                        print(f"💾 Промежуточное сохранение: {self.processed_groups}/{total_groups} групп обработано")
                    
                    print("-" * 60)
                    
                    # УВЕЛИЧЕННАЯ ПАУЗА между группами
                    time.sleep(GROUP_PROCESSING_DELAY)
                    
                except KeyboardInterrupt:
                    print(f"\n⚠️ Прерывание пользователем на группе {current_num}")
                    self.save_progress()
                    print(f"💾 Прогресс сохранен. Для продолжения установите RESUME_FROM_GROUP = {index}")
                    return
                    
                except Exception as e:
                    error_msg = f"Ошибка при обработке группы {group_id}: {e}"
                    print(f"   ❌ {error_msg}")
                    self.log_error(error_msg)
                    self.skipped_groups.append(group_id)
                    self.processed_groups += 1
                    time.sleep(GROUP_PROCESSING_DELAY)
                    continue
        
        except Exception as e:
            error_msg = f"Критическая ошибка: {e}"
            print(f"❌ {error_msg}")
            self.log_error(error_msg)
            self.save_progress()
            return
        
        # Финальное сохранение
        self.save_final_results()
        self.print_statistics(total_groups)
    
    def print_statistics(self, total: int):
        """Вывод статистики"""
        print("="*50)
        print("📈 ИТОГОВАЯ СТАТИСТИКА")
        print("="*50)
        print(f"Всего групп в списке: {total}")
        print(f"Обработано групп: {self.processed_groups}")
        print(f"Пропущено групп: {len(self.skipped_groups)}")
        print(f"Групп со скрытыми участниками: {len(self.hidden_members_groups)}")
        print(f"Групп с английскими символами: {len(self.non_russian_names_groups)}")
        print(f"Групп без нарушений: {self.processed_groups - len(set(self.hidden_members_groups + self.non_russian_names_groups))}")
        print(f"Всего API запросов: {self.total_requests}")
        print(f"Превышений лимита: {self.rate_limit_hits}")
        print("="*50)


def main():
    print("🚀 Скрипт проверки сообществ ВКонтакте")
    print("="*50)
    
    if not VK_TOKEN or VK_TOKEN == 'ваш_токен_здесь':
        print("❌ Укажите токен доступа в настройках!")
        return
    
    if not GROUP_IDS.strip():
        print("❌ Список групп не указан в настройках!")
        return
    
    checker = VKCommunitiesChecker(VK_TOKEN)
    checker.check_communities(GROUP_IDS)


if __name__ == "__main__":
    main()