- /rag/commands/expert.py - /core/services/__init__.py - /core/services/embedding_service.py - /core/services/file_service.py - /core/services/giga_client.py - /core/services/kb_service.py - /core/services/postgres_service.py - /core/services/qdrant_service.py - /core/services/reranker_service.py - /core/functions/__init__.py - /core/functions/check_consistency.py - /core/functions/check_spelling.py - /core/functions/critique_answer.py - /core/functions/expand_query.py - /core/functions/extract_metrics.py - /core/functions/file_processor.py - /core/functions/generate_document.py - /core/functions/intent_classify.py - /core/functions/rerank_context.py - /core/functions/summarize_document.py - /core/utils/__init__.py - /core/utils/arg_parser.py - /core/utils/config_loader.py - /core/utils/layout_converter.py - /core/utils/logger.py - /core/utils/text_utils.py - /core/utils/web_utils.py - /bots/commands/__init__.py - /bots/commands/base.py - /bots/commands/create.py - /core/commands/global_remove.py - /core/commands/help.py - /core/commands/info.py - /core/commands/kb.py - /core/commands/learn.py - /core/commands/other.py - /core/commands/registry.py - /core/commands/stats.py - /core/commands/template.py - /core/handlers/__init__.py - /core/handlers/message_handler.py - /core/workers/__init__.py - /core/workers/indexing_worker.py - /core/xmpp/__init__.py - /core/xmpp/client.py - /bots/commands/global_remove.py - /bots/commands/help.py - /bots/commands/info.py - /bots/commands/kb.py - /bots/commands/registry.py - /bots/commands/other.py - /bots/commands/template.py - /bots/commands/learn.py - /bots/commands/stats.py - /bots/handlers/message_handler.py - /bots/handlers/__init__.py - /bots/workers/__init__.py - /bots/workers/indexing_worker.py - /bots/xmpp/__init__.py - /bots/xmpp/client.py - /rag/services/qdrant_service.py - /rag/services/giga_client.py - /rag/services/embedding_service.py - /rag/services/reranker_service.py - /rag/services/__init__.py - /rag/services/file_service.py - /rag/services/postgres_service.py - /rag/services/kb_service.py - /rag/functions/check_spelling.py - /rag/functions/__init__.py - /rag/functions/extract_metrics.py - /rag/functions/generate_document.py - /rag/functions/expand_query.py - /rag/functions/critique_answer.py - /rag/functions/file_processor.py - /rag/functions/check_consistency.py - /rag/functions/summarize_document.py - /rag/functions/rerank_context.py - /rag/functions/intent_classify.py - /rag/utils/__init__.py - /rag/utils/layout_converter.py - /rag/utils/text_utils.py - /rag/utils/arg_parser.py - /rag/utils/config_loader.py - /rag/utils/logger.py - /rag/utils/web_utils.py
107 lines
5.8 KiB
Python
107 lines
5.8 KiB
Python
# -*- coding: utf-8 -*-
|
||
"""
|
||
Команда !global_remove – удаление документа из глобальной базы знаний по его названию.
|
||
Только для администраторов, работает только в личном чате.
|
||
|
||
Изменения для PostgreSQL (asyncpg):
|
||
- Замена курсоров на прямые запросы conn.fetch / conn.execute.
|
||
- Использование параметризованных запросов с $1, $2 и ANY($1) для массовых операций.
|
||
- Удаление синтаксиса, специфичного для MySQL/MariaDB.
|
||
"""
|
||
|
||
import logging
|
||
from core.commands.base import Command
|
||
|
||
logger = logging.getLogger(__name__)
|
||
|
||
|
||
class GlobalRemoveCommand(Command):
|
||
"""Удаляет глобальный документ по точному совпадению source_name."""
|
||
|
||
name = "!global_remove"
|
||
aliases = ["/global_remove"]
|
||
permission = "admin"
|
||
|
||
async def execute(self, msg, args: str, user_jid: str, room_jid: str = None):
|
||
"""
|
||
Алгоритм:
|
||
1. Проверка прав администратора.
|
||
2. Проверка, что команда вызвана в личном чате (room_jid is None).
|
||
3. Разбор аргументов, извлечение названия документа (в кавычках или без).
|
||
4. Поиск всех глобальных документов с таким source_name.
|
||
5. Удаление найденных документов из Qdrant (по doc_id).
|
||
6. Удаление записей из таблиц document_access и documents в PostgreSQL.
|
||
"""
|
||
# 1. Проверка прав администратора
|
||
if not self.check_permission(user_jid):
|
||
reply = msg.reply("⛔ У вас нет прав на удаление глобальных документов.")
|
||
if reply:
|
||
reply.send() # исправлено
|
||
else:
|
||
logger.error("Не удалось отправить сообщение о недостатке прав для global_remove")
|
||
return
|
||
|
||
# 2. Команда работает только в личном чате
|
||
if room_jid:
|
||
reply = msg.reply("⚠️ Команда !global_remove работает только в личном чате.")
|
||
if reply:
|
||
reply.send() # исправлено
|
||
else:
|
||
logger.error("Не удалось отправить сообщение об ограничении !global_remove")
|
||
return
|
||
|
||
# 3. Разбор аргументов команды
|
||
_, flags, pos = self.parse_args(args)
|
||
if not pos:
|
||
reply = msg.reply("❌ Укажите название документа для удаления. Пример: `!global_remove \"ГОСТ Р 12345\"`")
|
||
if reply:
|
||
reply.send() # исправлено
|
||
else:
|
||
logger.error("Не удалось отправить сообщение о необходимости указать название документа")
|
||
return
|
||
|
||
source_name = " ".join(pos).strip()
|
||
# Удаляем возможные кавычки, если пользователь заключил название в них
|
||
if source_name.startswith('"') and source_name.endswith('"'):
|
||
source_name = source_name[1:-1]
|
||
|
||
# 4. Поиск глобальных документов с указанным названием
|
||
async with self.bot.db.pool.acquire() as conn:
|
||
rows = await conn.fetch(
|
||
"SELECT id FROM documents WHERE is_global = TRUE AND source_name = $1",
|
||
source_name
|
||
)
|
||
doc_ids = [row['id'] for row in rows]
|
||
|
||
if not doc_ids:
|
||
reply = msg.reply(f"📭 Глобальный документ с названием '{source_name}' не найден.")
|
||
if reply:
|
||
reply.send() # исправлено
|
||
else:
|
||
logger.error("Не удалось отправить сообщение о ненайденном глобальном документе")
|
||
return
|
||
|
||
# 5. Удаление из Qdrant (по каждому doc_id)
|
||
for doc_id in doc_ids:
|
||
self.bot.qdrant.delete_by_doc_id(doc_id)
|
||
logger.debug(f"Удалён из Qdrant doc_id={doc_id}")
|
||
|
||
# 6. Удаление из PostgreSQL (сначала связанные права доступа, затем сам документ)
|
||
async with self.bot.db.pool.acquire() as conn:
|
||
# Удаляем записи о доступе (если есть)
|
||
await conn.execute("DELETE FROM document_access WHERE doc_id = ANY($1)", doc_ids)
|
||
# Удаляем сами документы
|
||
await conn.execute("DELETE FROM documents WHERE id = ANY($1)", doc_ids)
|
||
|
||
logger.info(f"Администратор {user_jid} удалил глобальный документ '{source_name}' (doc_ids: {doc_ids})")
|
||
reply = msg.reply(f"✅ Глобальный документ '{source_name}' удалён из базы знаний.")
|
||
if reply:
|
||
reply.send() # исправлено
|
||
else:
|
||
logger.error("Не удалось отправить подтверждение удаления глобального документа")
|
||
|
||
def get_help(self) -> str:
|
||
return (
|
||
"Удаляет глобальный документ по названию (админ, только в личном чате). "
|
||
"Использование: `!global_remove \"Название документа\"`"
|
||
) |