Базовая обнова, запрет на комментирование фоток /ebalnik ну и поменял ai чтобы пиздел меньше, а не километровыми пастами

This commit is contained in:
Hitoshi-Hub 2026-04-23 12:39:49 +03:00
parent 06d97cbff0
commit 3bad14eac2
4 changed files with 43 additions and 8 deletions

0
.codex Normal file
View file

View file

@ -2,6 +2,7 @@ import asyncio
import os import os
import logging import logging
import sqlite3 import sqlite3
import re
from pathlib import Path from pathlib import Path
import aiohttp import aiohttp
@ -69,6 +70,8 @@ _CONTEXT_SIZE = 20
_MAX_PROMPT_CHARS = 8_000 _MAX_PROMPT_CHARS = 8_000
# Путь к SQLite базе (берётся из переменной окружения или из config) # Путь к SQLite базе (берётся из переменной окружения или из config)
_DB_PATH = os.getenv("CHAT_HISTORY_DB_PATH", "/db/chat_history.sqlite3") _DB_PATH = os.getenv("CHAT_HISTORY_DB_PATH", "/db/chat_history.sqlite3")
_MAX_REPLY_SENTENCES = 3
_MAX_REPLY_CHARS = 280
def _init_db() -> None: def _init_db() -> None:
@ -139,20 +142,49 @@ def _build_messages(chat_id: int, system_prompt: str, current_text: str, current
return messages 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: async def _generate_response(chat_id: int, user_text: str, user_name: str) -> str:
# Формируем запрос к /v1/chat/completions с историей чата и возвращаем текст первого ответа. # Формируем запрос к /v1/chat/completions с историей чата и возвращаем текст первого ответа.
url = f"{LLAMA_API_URL.rstrip('/')}/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) msgs = await asyncio.to_thread(_build_messages, chat_id, SYSTEM_PROMPT, user_text, user_name)
payload = { payload = {
"messages": msgs, "messages": msgs,
"max_tokens": 200, "max_tokens": 90,
"temperature": 0.8, "temperature": 0.8,
"top_p": 0.9, "top_p": 0.9,
} }
async with aiohttp.ClientSession() as session: async with aiohttp.ClientSession() as session:
async with session.post(url, json=payload, timeout=aiohttp.ClientTimeout(total=120)) as resp: async with session.post(url, json=payload, timeout=aiohttp.ClientTimeout(total=120)) as resp:
data = await resp.json() 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): 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) msgs = await asyncio.to_thread(_build_messages, chat_id, AUTOREPLY_SYSTEM_PROMPT, text, user_name)
payload = { payload = {
"messages": msgs, "messages": msgs,
"max_tokens": 150, "max_tokens": 70,
"temperature": 0.9, "temperature": 0.9,
"top_p": 0.9, "top_p": 0.9,
} }
async with aiohttp.ClientSession() as session: async with aiohttp.ClientSession() as session:
async with session.post(url, json=payload, timeout=aiohttp.ClientTimeout(total=120)) as resp: async with session.post(url, json=payload, timeout=aiohttp.ClientTimeout(total=120)) as resp:
data = await resp.json() data = await resp.json()
return data["choices"][0]["message"]["content"].strip() content = data["choices"][0]["message"]["content"].strip()
return _shorten_reply(content)
# Инициализация БД при импорте модуля # Инициализация БД при импорте модуля

View file

@ -9,7 +9,8 @@ logger = logging.getLogger(__name__)
CAPTIONS = [ CAPTIONS = [
"Hot stuff 🔥", "Hot stuff 🔥",
"Sexy content 😏" "Sexy content 😏",
"ШПАЧИХА"
] ]
async def handle_nude_cmd(message: types.Message): async def handle_nude_cmd(message: types.Message):

View file

@ -1145,6 +1145,10 @@ async def handle_keywords(message: Message):
chat_id = message.chat.id chat_id = message.chat.id
user_name = (message.from_user.first_name or "кент") if message.from_user else "кент" 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): if any(re.search(rf'\b{word}\b', text_lower) for word in config.KEYWORDS):
audio_path = config.BASE_DIR / "maket" / "Radiohead - Creep.mp3" audio_path = config.BASE_DIR / "maket" / "Radiohead - Creep.mp3"
if audio_path.exists(): 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) 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 import time as _time
now = _time.time() now = _time.time()
triggered = any(trigger in text_lower for trigger in config.AUTOREPLY_TRIGGERS) triggered = any(trigger in text_lower for trigger in config.AUTOREPLY_TRIGGERS)