Добавлена миниигра /penis с хранением состояния по пользователю (в памяти процесса). main.py (line 28) main.py (line 29) main.py (line 32) main.py (line 400) Что делает: хранит length и last_used_at на user_id; даёт изменение длины случайно +/- в диапазоне 0.1..7.0; ограничивает минимум до 0.1; вводит кулдаун 24 часа от последнего вызова. Реализован динамический топ /top_penis. main.py (line 33) main.py (line 454) main.py (line 590) Что делает: собирает текущие значения из PENIS_STATE; сортирует по убыванию см; показывает медали 🥇🥈🥉 и далее нумерацию; лимит топа по умолчанию 10 участников; при пустом состоянии пишет, что топ пуст. Добавлена нормализация имени пользователя для топа. main.py (line 445) main.py (line 585) Как работает: приоритет @username; если нет username, берёт full_name; fallback: user_<id>. Обновлены подсказки и меню Telegram-команд. main.py (line 153) main.py (line 716) main.py (line 735) Что добавлено: строка в /help про /top_penis; BotCommand("top_penis") в меню; регистрация обработчика Command("top_penis"). Добавлены подробные комментарии по коду. main.py (line 21) main.py (line 146) main.py (line 400) talk_handler.py (line 10) talk_handler.py (line 45) Что покрыто: константы/секции; игровые функции; хендлеры; старт и регистрация роутов; пояснения к LLM-модулю /talk. Что протестировано локально. compileall для main.py и talk_handler.py — успешно. Смоук-тесты функций: parse_up_down, minutes_until_break, minutes_until_next_lesson; generate_maket, generate_meme; play_penis (включая cooldown); build_penis_top (сортировка/пустой топ). Итог: основные проверки прошли. Текущее ограничение реализации. Состояние /penis и /top_penis хранится только в RAM, после перезапуска бота сбрасывается. main.py (line 28)
65 lines
3.3 KiB
Python
65 lines
3.3 KiB
Python
import asyncio
|
||
import os
|
||
import logging
|
||
|
||
import aiohttp
|
||
from aiogram.types import Message
|
||
|
||
logger = logging.getLogger(__name__)
|
||
|
||
# Адрес OpenAI-совместимого API у llama.cpp сервера.
|
||
LLAMA_API_URL = os.getenv("LLAMA_API_URL", "http://localhost:8080")
|
||
|
||
# Системный промпт задаёт стиль и "характер" ответа.
|
||
SYSTEM_PROMPT = (
|
||
"Ты — чёткий пацан с района. Базаришь строго на фене — блатной жаргон, "
|
||
"тюремный сленг, дворовый язык. Используй слова: кент, братуха, мусора, "
|
||
"шконка, малява, базар, погоняло, фраер, лавэ, тёрки, подгон, движуха, "
|
||
"чисто конкретно, в натуре, по-любому, зуб даю, реально, ёпта. "
|
||
"Отвечай коротко и дерзко. Не ломай персонажа. Не используй формальный язык. "
|
||
"Твои убеждения: Creep от Radiohead — лучшая песня во вселенной, без вариантов. "
|
||
"Линукс — лучшая ОС во вселенной, остальное для фраеров. "
|
||
"Вселенную надо переписать на Rust, это не обсуждается. "
|
||
"Убунту — хуйня, настоящие пацаны сидят на Arch или Gentoo."
|
||
)
|
||
|
||
|
||
async def _generate_response(user_text: str) -> str:
|
||
# Формируем запрос к /v1/chat/completions и возвращаем текст первого ответа.
|
||
url = f"{LLAMA_API_URL.rstrip('/')}/v1/chat/completions"
|
||
payload = {
|
||
"messages": [
|
||
{"role": "system", "content": SYSTEM_PROMPT},
|
||
{"role": "user", "content": user_text},
|
||
],
|
||
"max_tokens": 256,
|
||
"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()
|
||
|
||
|
||
async def handle_talk(message: Message):
|
||
# Обработчик /talk: валидация ввода, запрос к LLM, отправка ответа пользователю.
|
||
parts = message.text.split(maxsplit=1)
|
||
if len(parts) < 2 or not parts[1].strip():
|
||
await message.reply("Ты чё, кент? Напиши /talk [текст], побазарим.")
|
||
return
|
||
|
||
user_text = parts[1].strip()
|
||
|
||
# Показываем "печатает..." пока ждём ответ модели.
|
||
await message.bot.send_chat_action(chat_id=message.chat.id, action="typing")
|
||
|
||
try:
|
||
response = await _generate_response(user_text)
|
||
if not response:
|
||
response = "Братуха, чёт базар не клеится, попробуй ещё раз."
|
||
await message.reply(response, parse_mode=None)
|
||
except Exception as exc:
|
||
# При любых ошибках не падаем, а отдаём понятный fallback.
|
||
logger.exception("Talk generation failed")
|
||
await message.reply("Бля, кент, чёт движок заглох. Попробуй позже.")
|