Реализован функцоинал хранения сырых текстов поста в базе данных. Оформление поста происходит непосредственно перед его отправкой в канал.

- Реализованы методы `get_post_text_and_anonymity_by_message_id` и `get_post_text_and_anonymity_by_helper_id` в `PostRepository` для получения текста поста и флага анонимности.
- Обновлена модель `TelegramPost`, добавлено поле `is_anonymous`.
- Изменена схема базы данных для включения поля `is_anonymous` в таблицу `post_from_telegram_suggest`.
- Обновлены функции публикации постов в `PostPublishService` для учета анонимности.
- Добавлены тесты для проверки новых функций и корректности работы с анонимностью.
This commit is contained in:
2026-01-23 12:12:21 +03:00
parent c6ba90552d
commit 89022aedaf
12 changed files with 1064 additions and 48 deletions

148
scripts/clean_post_text.py Executable file
View File

@@ -0,0 +1,148 @@
#!/usr/bin/env python3
"""
Скрипт для приведения текста постов к "сырому" виду.
Удаляет форматирование, добавленное функцией get_text_message(), оставляя только исходный текст.
"""
import argparse
import asyncio
import html
import os
import re
import sys
from pathlib import Path
project_root = Path(__file__).resolve().parent.parent
sys.path.insert(0, str(project_root))
import aiosqlite
from logs.custom_logger import logger
DEFAULT_DB_PATH = "database/tg-bot-database.db"
# Паттерны для определения форматированного текста
PREFIX = "Пост из ТГ:\n"
ANONYMOUS_SUFFIX = "\n\nПост опубликован анонимно"
AUTHOR_SUFFIX_PATTERN = re.compile(r"\n\nАвтор поста: .+$")
def extract_raw_text(formatted_text: str) -> str:
"""
Извлекает сырой текст из форматированного текста поста.
Args:
formatted_text: Форматированный текст поста
Returns:
str: Сырой текст или исходный текст, если форматирование не обнаружено
"""
if not formatted_text:
return ""
# Проверяем, начинается ли текст с префикса
if not formatted_text.startswith(PREFIX):
# Текст уже в сыром виде или имеет другой формат
return formatted_text
# Извлекаем текст после префикса
text_after_prefix = formatted_text[len(PREFIX):]
# Проверяем, заканчивается ли текст на "Пост опубликован анонимно"
if text_after_prefix.endswith(ANONYMOUS_SUFFIX):
raw_text = text_after_prefix[:-len(ANONYMOUS_SUFFIX)]
# Проверяем, заканчивается ли текст на "Автор поста: ..."
elif AUTHOR_SUFFIX_PATTERN.search(text_after_prefix):
raw_text = AUTHOR_SUFFIX_PATTERN.sub("", text_after_prefix)
else:
# Не удалось определить формат, возвращаем текст без префикса
raw_text = text_after_prefix
# Декодируем HTML-экранирование
raw_text = html.unescape(raw_text)
return raw_text
async def main(db_path: str, dry_run: bool = False) -> None:
db_path = os.path.abspath(db_path)
if not os.path.exists(db_path):
logger.error("База данных не найдена: %s", db_path)
print(f"Ошибка: база данных не найдена: {db_path}")
return
async with aiosqlite.connect(db_path) as conn:
await conn.execute("PRAGMA foreign_keys = ON")
# Получаем все записи с текстом
cursor = await conn.execute(
"SELECT message_id, text FROM post_from_telegram_suggest WHERE text IS NOT NULL AND text != ''"
)
posts = await cursor.fetchall()
await cursor.close()
updated_count = 0
skipped_count = 0
error_count = 0
print(f"Найдено записей для обработки: {len(posts)}")
if dry_run:
print("РЕЖИМ ПРОВЕРКИ (dry-run): изменения не будут сохранены")
# Обрабатываем каждую запись
for message_id, formatted_text in posts:
try:
# Извлекаем сырой текст
raw_text = extract_raw_text(formatted_text)
# Проверяем, изменился ли текст
if raw_text == formatted_text:
skipped_count += 1
continue
if dry_run:
print(f"\n[DRY-RUN] message_id={message_id}:")
print(f" Было: {formatted_text[:100]}...")
print(f" Станет: {raw_text[:100]}...")
else:
# Обновляем запись
await conn.execute(
"UPDATE post_from_telegram_suggest SET text = ? WHERE message_id = ?",
(raw_text, message_id)
)
updated_count += 1
except Exception as e:
logger.error(f"Ошибка при обработке поста message_id={message_id}: {e}")
error_count += 1
if not dry_run:
await conn.commit()
total_processed = updated_count + skipped_count + error_count
logger.info(
f"Обработка завершена. Всего записей: {total_processed}, "
f"обновлено: {updated_count}, пропущено: {skipped_count}, ошибок: {error_count}"
)
print(f"\nОбработка завершена:")
print(f" - Всего записей: {total_processed}")
print(f" - Обновлено: {updated_count}")
print(f" - Пропущено (уже в сыром виде): {skipped_count}")
print(f" - Ошибок: {error_count}")
if __name__ == "__main__":
parser = argparse.ArgumentParser(
description="Приведение текста постов к 'сырому' виду"
)
parser.add_argument(
"--db",
default=os.environ.get("DB_PATH", DEFAULT_DB_PATH),
help="Путь к БД (или DB_PATH)",
)
parser.add_argument(
"--dry-run",
action="store_true",
help="Режим проверки без сохранения изменений",
)
args = parser.parse_args()
asyncio.run(main(args.db, args.dry_run))