From 3bad14eac2703a133f8be97677d9d90139a21711 Mon Sep 17 00:00:00 2001 From: Hitoshi-Hub Date: Thu, 23 Apr 2026 12:39:49 +0300 Subject: [PATCH] =?UTF-8?q?=D0=91=D0=B0=D0=B7=D0=BE=D0=B2=D0=B0=D1=8F=20?= =?UTF-8?q?=D0=BE=D0=B1=D0=BD=D0=BE=D0=B2=D0=B0,=20=D0=B7=D0=B0=D0=BF?= =?UTF-8?q?=D1=80=D0=B5=D1=82=20=D0=BD=D0=B0=20=D0=BA=D0=BE=D0=BC=D0=BC?= =?UTF-8?q?=D0=B5=D0=BD=D1=82=D0=B8=D1=80=D0=BE=D0=B2=D0=B0=D0=BD=D0=B8?= =?UTF-8?q?=D0=B5=20=D1=84=D0=BE=D1=82=D0=BE=D0=BA=20/ebalnik=20=D0=BD?= =?UTF-8?q?=D1=83=20=D0=B8=20=D0=BF=D0=BE=D0=BC=D0=B5=D0=BD=D1=8F=D0=BB=20?= =?UTF-8?q?ai=20=D1=87=D1=82=D0=BE=D0=B1=D1=8B=20=D0=BF=D0=B8=D0=B7=D0=B4?= =?UTF-8?q?=D0=B5=D0=BB=20=D0=BC=D0=B5=D0=BD=D1=8C=D1=88=D0=B5,=20=D0=B0?= =?UTF-8?q?=20=D0=BD=D0=B5=20=D0=BA=D0=B8=D0=BB=D0=BE=D0=BC=D0=B5=D1=82?= =?UTF-8?q?=D1=80=D0=BE=D0=B2=D1=8B=D0=BC=D0=B8=20=D0=BF=D0=B0=D1=81=D1=82?= =?UTF-8?q?=D0=B0=D0=BC=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .codex | 0 AI/talk_handler.py | 41 +++++++++++++++++++++++++++++++++++++---- games/nude.py | 3 ++- main.py | 7 ++++--- 4 files changed, 43 insertions(+), 8 deletions(-) create mode 100644 .codex diff --git a/.codex b/.codex new file mode 100644 index 0000000..e69de29 diff --git a/AI/talk_handler.py b/AI/talk_handler.py index 2758c97..348f081 100644 --- a/AI/talk_handler.py +++ b/AI/talk_handler.py @@ -2,6 +2,7 @@ import asyncio import os import logging import sqlite3 +import re from pathlib import Path import aiohttp @@ -69,6 +70,8 @@ _CONTEXT_SIZE = 20 _MAX_PROMPT_CHARS = 8_000 # Путь к SQLite базе (берётся из переменной окружения или из config) _DB_PATH = os.getenv("CHAT_HISTORY_DB_PATH", "/db/chat_history.sqlite3") +_MAX_REPLY_SENTENCES = 3 +_MAX_REPLY_CHARS = 280 def _init_db() -> None: @@ -139,20 +142,49 @@ def _build_messages(chat_id: int, system_prompt: str, current_text: str, current return messages +def _shorten_reply(text: str, max_sentences: int = _MAX_REPLY_SENTENCES, max_chars: int = _MAX_REPLY_CHARS) -> str: + """Ограничивает ответ до 1-3 предложений и разумной длины.""" + cleaned = " ".join((text or "").replace("\n", " ").split()).strip() + if not cleaned: + return "Братуха, коротко: чёт пусто получилось." + + # Делим по окончаниям предложений, сохраняя знаки. + parts = re.split(r"(?<=[.!?…])\s+", cleaned) + sentences = [p.strip() for p in parts if p.strip()] + + if not sentences: + # На случай ответа без пунктуации. + words = cleaned.split() + return " ".join(words[:25]).strip() + + short = " ".join(sentences[:max_sentences]).strip() + + # Дополнительный предохранитель по символам. + if len(short) > max_chars: + clipped = short[:max_chars].rstrip() + last_break = max(clipped.rfind("."), clipped.rfind("!"), clipped.rfind("?"), clipped.rfind("…")) + if last_break >= 40: + clipped = clipped[: last_break + 1].rstrip() + short = clipped + + return short + + async def _generate_response(chat_id: int, user_text: str, user_name: str) -> str: # Формируем запрос к /v1/chat/completions с историей чата и возвращаем текст первого ответа. url = f"{LLAMA_API_URL.rstrip('/')}/v1/chat/completions" msgs = await asyncio.to_thread(_build_messages, chat_id, SYSTEM_PROMPT, user_text, user_name) payload = { "messages": msgs, - "max_tokens": 200, + "max_tokens": 90, "temperature": 0.8, "top_p": 0.9, } async with aiohttp.ClientSession() as session: async with session.post(url, json=payload, timeout=aiohttp.ClientTimeout(total=120)) as resp: data = await resp.json() - return data["choices"][0]["message"]["content"].strip() + content = data["choices"][0]["message"]["content"].strip() + return _shorten_reply(content) async def handle_talk(message: Message): @@ -230,14 +262,15 @@ async def generate_autoreply(chat_id: int, text: str, user_name: str) -> str: msgs = await asyncio.to_thread(_build_messages, chat_id, AUTOREPLY_SYSTEM_PROMPT, text, user_name) payload = { "messages": msgs, - "max_tokens": 150, + "max_tokens": 70, "temperature": 0.9, "top_p": 0.9, } async with aiohttp.ClientSession() as session: async with session.post(url, json=payload, timeout=aiohttp.ClientTimeout(total=120)) as resp: data = await resp.json() - return data["choices"][0]["message"]["content"].strip() + content = data["choices"][0]["message"]["content"].strip() + return _shorten_reply(content) # Инициализация БД при импорте модуля diff --git a/games/nude.py b/games/nude.py index 280bbf4..bfff1a3 100644 --- a/games/nude.py +++ b/games/nude.py @@ -9,7 +9,8 @@ logger = logging.getLogger(__name__) CAPTIONS = [ "Hot stuff 🔥", - "Sexy content 😏" + "Sexy content 😏", + "ШПАЧИХА" ] async def handle_nude_cmd(message: types.Message): diff --git a/main.py b/main.py index 7891bde..1e54a5a 100644 --- a/main.py +++ b/main.py @@ -1145,6 +1145,10 @@ async def handle_keywords(message: Message): chat_id = message.chat.id user_name = (message.from_user.first_name or "кент") if message.from_user else "кент" + # /ebalnik должен глушить все авто-реакции в чате, включая медиа/фото-ветки. + if chat_id in _autoreply_disabled_chats: + return + if any(re.search(rf'\b{word}\b', text_lower) for word in config.KEYWORDS): audio_path = config.BASE_DIR / "maket" / "Radiohead - Creep.mp3" if audio_path.exists(): @@ -1171,9 +1175,6 @@ async def handle_keywords(message: Message): # Записываем каждое обычное сообщение в историю чата await asyncio.to_thread(push_message, chat_id, "user", user_name, message.text) - if chat_id in _autoreply_disabled_chats: - return - import time as _time now = _time.time() triggered = any(trigger in text_lower for trigger in config.AUTOREPLY_TRIGGERS)