From 8b4659cffb1d38bdada6c89757b0fecee5c0ac30 Mon Sep 17 00:00:00 2001 From: ysv Date: Fri, 23 Oct 2026 12:28:40 +0300 Subject: [PATCH] =?UTF-8?q?def=20get=5Flatest=5Fmessages(group=5Fname:=20s?= =?UTF-8?q?tr,=20count:=20int=20=3D=2010)=20->=20list[dict]:=20=20=20=20?= =?UTF-8?q?=20"""=20=20=20=20=20=D0=9F=D0=BE=D0=BB=D1=83=D1=87=D0=B8=D1=82?= =?UTF-8?q?=D1=8C=20=D0=B7=D0=B0=D0=B4=D0=B0=D0=BD=D0=BD=D0=BE=D0=B5=20?= =?UTF-8?q?=D0=BA=D0=BE=D0=BB=D0=B8=D1=87=D0=B5=D1=81=D1=82=D0=B2=D0=BE=20?= =?UTF-8?q?=D0=BF=D0=BE=D1=81=D0=BB=D0=B5=D0=B4=D0=BD=D0=B8=D1=85=20=D1=81?= =?UTF-8?q?=D0=BE=D0=BE=D0=B1=D1=89=D0=B5=D0=BD=D0=B8=D0=B9=20=D0=B8=D0=B7?= =?UTF-8?q?=20=D1=83=D0=BA=D0=B0=D0=B7=D0=B0=D0=BD=D0=BD=D0=BE=D0=B9=20?= =?UTF-8?q?=D0=B3=D1=80=D1=83=D0=BF=D0=BF=D1=8B.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Возвращает список словарей с полями: Msg-Num, Message-ID, Subject, Date, Name, Mail. Сообщения строго отсортированы от самых новых к старым. --- mcp/nntp/nntp_mcp.py | 66 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/mcp/nntp/nntp_mcp.py b/mcp/nntp/nntp_mcp.py index 8a8cd6d..c3c2956 100644 --- a/mcp/nntp/nntp_mcp.py +++ b/mcp/nntp/nntp_mcp.py @@ -362,6 +362,72 @@ def save_text_to_file(filename: str, content: str) -> str: return f"ERROR: {str(e)}" +@mcp.tool() +def get_latest_messages(group_name: str, count: int = 10) -> list[dict]: + """ + Получить заданное количество последних сообщений из указанной группы. + + Возвращает список словарей с полями: Msg-Num, Message-ID, Subject, Date, Name, Mail. + Сообщения строго отсортированы от самых новых к старым. + """ + from email.utils import parseaddr + + try: + latest_messages = [] + with nntplib.NNTP(NNTP_SERVER, port=PORT, timeout=15) as s: + # Получаем информацию о группе и номера сообщений + resp, total_count, first, last, name = s.group(group_name) + first_num = int(first) + last_num = int(last) + + if first_num > last_num or total_count == 0: + return [] + + # Берем диапазон с запасом, чтобы гарантированно набрать count живых сообщений + # (на случай, если часть постов между first и last были удалены) + start_msg = max(first_num, last_num - (count * 2) + 1) + + # Получаем овервью (заголовки) сообщений в выбранном диапазоне + resp, overviews = s.over((str(start_msg), str(last_num))) + + # СТРОГАЯ СОРТИРОВКА: переводим ключи в int и сортируем по убыванию (от новых к старым) + sorted_overviews = sorted(overviews, key=lambda x: int(x[0]), reverse=True) + + # Обходим сообщения уже в гарантированно правильном порядке + for msg_num, over in sorted_overviews: + # Извлекаем message-id из овервью или генерируем запасной + raw_msg_id = over.get('message-id', f"<{msg_num}@{NNTP_SERVER}>") + + raw_author = over.get('from', '') + raw_subject = over.get('subject', '') + raw_date = over.get('date', '') + + # Декодируем MIME-заголовки с помощью существующих функций проекта + author = decode_mime_header(raw_author) + subject = decode_mime_header(raw_subject) + date = decode_mime_header(raw_date) + + # Разделяем From на Имя и Почту + name_part, mail_part = parseaddr(author) + + latest_messages.append({ + "Msg-Num": str(msg_num), # Локальный порядковый номер в группе + "Message-ID": raw_msg_id, # Глобальный уникальный идентификатор + "Subject": subject if subject else "[Без темы]", + "Date": date, + "Name": name_part if name_part else "[Без имени]", + "Mail": mail_part if mail_part else "[Без почты]" + }) + + # Прерываем цикл, как только набрали нужное количество + if len(latest_messages) >= count: + break + + return latest_messages + except Exception as e: + return [{"error": f"Ошибка NNTP при получении последних сообщений: {e}"}] + + if __name__ == "__main__": # Для современного пакета MCPServer (MCP v2.x)