Add new file

This commit is contained in:
Markov Andrey
2026-06-30 09:11:44 +00:00
parent ed06d6be9f
commit e65a0f4922

View File

@@ -0,0 +1,169 @@
# -*- coding: utf-8 -*-
"""
Обработка файлов: отправка пользователю через HTTP Upload и хирургическая замена в DOCX.
Функции: send_file_to_user, handle_ai_file_request.
АДАПТАЦИЯ: эти функции уже работают через HTTP (загружают файлы в ejabberd).
Они не требуют изменений, так как работают с локальными файлами.
Но я добавлю комментарии для ясности.
"""
import asyncio
import os
import re
import shutil
import time
import random
import string
import logging
logger = logging.getLogger(__name__)
async def send_file_to_user(bot, msg, file_path, prefix="", suffix=None):
"""
Отправляет файл пользователю через HTTP Upload (XEP-0363).
Аргументы:
bot: экземпляр бота
msg: объект XMPP-сообщения
file_path: путь к файлу на диске
prefix: текст перед ссылкой
suffix: суффикс для имени файла
Возвращает:
bool: True при успехе, False при ошибке
"""
if not os.path.exists(file_path):
logger.error(f"Файл не найден: {file_path}")
return False
original_name = os.path.basename(file_path)
name, ext = os.path.splitext(original_name)
# Санитизация суффикса (только буквы, цифры, подчёркивание, дефис)
if suffix:
suffix = re.sub(r'[^a-zA-Zа-яА-ЯёЁ0-9_-]', '', suffix)
suffix = suffix.strip('.')
if not suffix or suffix == '_':
suffix = '_sanitized'
if not suffix.startswith('_'):
suffix = '_' + suffix
new_name = f"{name}{suffix}{ext}"
new_path = os.path.join(os.path.dirname(file_path), new_name)
os.rename(file_path, new_path)
file_path = new_path
try:
# Загружаем файл через XEP-0363 (HTTP Upload)
result = await bot.plugin['xep_0363'].upload_file(file_path)
if isinstance(result, dict):
file_url = result.get('get_url', result.get('url', str(result)))
else:
file_url = str(result)
except Exception as e:
logger.error(f"Ошибка загрузки файла через XEP-0363: {e}", exc_info=True)
error_reply = msg.reply("Не удалось загрузить файл. Проверьте настройки HTTP Upload.")
if error_reply:
await error_reply.send()
return False
reply = msg.reply(f"{prefix}\n📄 {file_url}")
if reply:
reply.send()
else:
logger.error("Не удалось создать reply для ссылки на файл")
return True
async def handle_ai_file_request(bot, msg, ai_response, user_jid, room_jid=None, body=None, suffix=None):
"""
Обрабатывает теги [SURGICAL_REPLACE], [REWRITE], [FILE] из ответа AI.
Поддерживает три режима:
- SURGICAL: замена текста в существующем DOCX
- REWRITE: создание нового DOCX из текста
- FILE: создание нового DOCX (общий случай)
"""
key = (user_jid, room_jid) if room_jid is not None else user_jid
surgical_match = re.search(r'\[SURGICAL_REPLACE\](.*?)\[/SURGICAL_REPLACE\]', ai_response, re.DOTALL)
rewrite_match = re.search(r'\[REWRITE\](.*?)\[/REWRITE\]', ai_response, re.DOTALL)
file_match = re.search(r'\[FILE\](.*?)\[/FILE\]', ai_response, re.DOTALL)
# ---- SURGICAL_REPLACE ----
if surgical_match:
content = surgical_match.group(1).strip()
replacements = {}
for line in content.split('\n'):
line = line.strip()
if '|||' in line:
old, new = line.split('|||', 1)
replacements[old.strip()] = new.strip()
if not replacements:
reply = msg.reply("⚠️ Не найдено пар 'старое ||| новое' в теге SURGICAL_REPLACE.")
if reply:
reply.send()
return
original_path = bot._last_file_path.get(key)
if not original_path or not os.path.exists(original_path):
reply = msg.reply("❌ Нет загруженного документа для замены. Сначала отправьте файл (без !learn).")
if reply:
reply.send()
return
new_path = await asyncio.to_thread(bot.files.surgical_replace, original_path, replacements)
if new_path:
await send_file_to_user(bot, msg, new_path, prefix="✅ Замена выполнена:", suffix="surgical")
try:
os.unlink(new_path)
except Exception as e:
logger.warning(f"Не удалось удалить временный файл {new_path}: {e}")
else:
reply = msg.reply("❌ Ошибка при хирургической замене. Проверьте формат документа.")
if reply:
reply.send()
return
# ---- REWRITE ----
if rewrite_match:
content = rewrite_match.group(1).strip()
title = "Сгенерированный документ"
title_match = re.search(r'\[TITLE\](.*?)\[/TITLE\]', ai_response, re.DOTALL)
if title_match:
title = title_match.group(1).strip()
new_path = await asyncio.to_thread(bot.files.create_docx, content, title, user_jid, mode="REWRITE")
if new_path:
await send_file_to_user(bot, msg, new_path, prefix="📄 Документ создан:", suffix="rewrite")
try:
os.unlink(new_path)
except Exception as e:
logger.warning(f"Не удалось удалить временный файл {new_path}: {e}")
else:
reply = msg.reply("Не удалось создать документ.")
if reply:
reply.send()
return
# ---- FILE ----
if file_match:
content = file_match.group(1).strip()
title = "Файл"
title_match = re.search(r'\[TITLE\](.*?)\[/TITLE\]', ai_response, re.DOTALL)
if title_match:
title = title_match.group(1).strip()
new_path = await asyncio.to_thread(bot.files.create_docx, content, title, user_jid, mode="FILE")
if new_path:
await send_file_to_user(bot, msg, new_path, prefix="📄 Файл создан:", suffix="file")
try:
os.unlink(new_path)
except Exception as e:
logger.warning(f"Не удалось удалить временный файл {new_path}: {e}")
else:
reply = msg.reply("Не удалось создать файл.")
if reply:
reply.send()
return
logger.debug(f"Ответ AI не содержит тегов файлов: {ai_response[:200]}")