forked from zovos/bot_tg
Базовая обнова, запрет на комментирование фоток /ebalnik ну и поменял ai чтобы пиздел меньше, а не километровыми пастами
This commit is contained in:
parent
06d97cbff0
commit
3bad14eac2
4 changed files with 43 additions and 8 deletions
0
.codex
Normal file
0
.codex
Normal 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)
|
||||||
|
|
||||||
|
|
||||||
# Инициализация БД при импорте модуля
|
# Инициализация БД при импорте модуля
|
||||||
|
|
|
||||||
|
|
@ -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):
|
||||||
|
|
|
||||||
7
main.py
7
main.py
|
|
@ -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)
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue