import re def truncate_text(text, max_length): """Обрезает текст до максимальной длины с учетом границ слов""" if text is None or not text: return "" if len(text) <= max_length: return text # Ищем последний пробел перед максимальной длиной truncated = text[:max_length] last_space = truncated.rfind(' ') if last_space > 0: return truncated[:last_space] + "..." return truncated[:max_length-3] + "..." def safe_text(text): """Обеспечивает безопасное использование текста (заменяет None на пустую строку)""" return text if text is not None else "" def replace_vk_links(text): """ Заменяет ссылки на сообщества ВК в форматах: [club123|Название сообщества] -> Название сообщества [id456|Имя пользователя] -> Имя пользователя [url|текст] -> текст (для Telegram) """ if not text: return text # Сначала обрабатываем ссылки формата [url|текст] и преобразуем их в HTML-ссылки def replace_url_link(match): url = match.group(1) link_text = match.group(2) return f'{link_text}' # Регулярное выражение для поиска ссылок формата [url|текст] # Исключаем случаи, когда url начинается с club или id и цифр, за которыми сразу следует | (это обрабатывается отдельно) url_pattern = r'\[(?!club\d+\||id\d+\|)([^\|]+)\|([^\]]+)\]' text = re.sub(url_pattern, replace_url_link, text) # Регулярное выражение для поиска ссылок формата [типID|Текст] (ВК-специфичные) vk_pattern = r'\[(?:club|id)\d+\|([^\]]+)\]' # Заменяем все вхождения на текст внутри ссылки return re.sub(vk_pattern, r'\1', text) def remove_html_tags(text): """Удаляет HTML-теги с учетом специальной обработки

и """ if not text: return text # Заменяем

и

на переносы строк text = re.sub(r'', '\n', text, flags=re.IGNORECASE) # Обрабатываем ссылки Текст -> Текст (URL) def replace_a_tag(match): url = match.group(1) link_text = match.group(2) return f"{link_text} ({url})" text = re.sub( r']*>(.*?)', replace_a_tag, text, flags=re.IGNORECASE | re.DOTALL ) # Удаляем все оставшиеся HTML-теги text = re.sub(r'<[^>]+>', '', text) # Схлопываем множественные переносы строк text = re.sub(r'\n+', '\n', text) return text.strip() def get_event_text(text, limit=950): """ Обрабатывает текст: удаляет HTML-теги и VK-ссылки, обрезает до указанной длины :param text: исходный текст :param limit: максимальная длина текста (по умолчанию 950) :return: обработанный текст """ if not text: return "" # Удаляем HTML-теги cleaned_text = remove_html_tags(text) # Обрабатываем VK-ссылки cleaned_text = replace_vk_links(cleaned_text) # Обрезаем текст до указанной длины return truncate_text(cleaned_text, limit) def get_post_text(post_data, max_caption_length=1000, max_text_length=4000): """ Форматирует пост для публикации в Telegram Возвращает словарь с элементами: { 'base_text': основной текст поста, 'image_url': URL изображения (или None), 'vk_link': HTML-ссылка на оригинал в ВК, 'caption': подпись для изображения (с учетом ограничений длины), 'text_message': текстовое сообщение (с учетом ограничений длины), 'has_poll': есть ли опрос, 'is_event': является ли пост событием, 'poll_question': вопрос опроса, 'poll_options': варианты ответов опроса, 'poll_multiple': разрешен ли множественный выбор } """ # Извлекаем данные из кортежа vk_post_id = post_data[0] text_content = safe_text(post_data[1]) image_url = safe_text(post_data[2]) vk_post_url = safe_text(post_data[3]) poll_question = safe_text(post_data[4]) poll_options = safe_text(post_data[5]) poll_multiple = post_data[6] is_event = post_data[7] has_poll = post_data[8] # Извлекаем значение is_poll # Обрабатываем текст: заменяем ссылки на сообщества ВК text_content = replace_vk_links(text_content) # Базовый текст - только содержимое поста base_text = text_content vk_link = f'Оригинал в ВК' if vk_post_url else "" # Формируем подпись для изображения caption = None if image_url: full_caption = f"{base_text}\n\n{vk_link}" if vk_link else base_text if len(full_caption) > max_caption_length: available_length = max_caption_length - (len(vk_link) + 10) if vk_link else 0 truncated_text = truncate_text(base_text, available_length) caption = f"{truncated_text}\n\n{vk_link}" if vk_link else truncated_text else: caption = full_caption # Формируем текстовое сообщение (для постов без изображения) text_message = None if not image_url: full_message = f"{base_text}\n\n{vk_link}" if vk_link else base_text if len(full_message) > max_text_length: available_length = max_text_length - (len(vk_link) + 10) if vk_link else 0 truncated_text = truncate_text(base_text, available_length) text_message = f"{truncated_text}\n\n{vk_link}" if vk_link else truncated_text else: text_message = full_message return { 'vk_post_id': vk_post_id, 'base_text': base_text, 'image_url': image_url if image_url else None, 'vk_link': vk_link, 'caption': caption, 'text_message': text_message, 'has_poll': has_poll, # Используем прямое значение из кортежа 'is_event': is_event, 'poll_question': poll_question, 'poll_options': poll_options, 'poll_multiple': poll_multiple }