ИИ-бот #4
6 changed files with 85 additions and 5 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
|
@ -18,3 +18,5 @@ db/
|
|||
|
||||
.pytest_cache/
|
||||
.mypy_cache/
|
||||
|
||||
AI/models/
|
||||
|
|
|
|||
59
AI/talk_handler.py
Normal file
59
AI/talk_handler.py
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
import asyncio
|
||||
import os
|
||||
import logging
|
||||
|
||||
import aiohttp
|
||||
from aiogram.types import Message
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
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:
|
||||
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):
|
||||
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:
|
||||
logger.exception("Talk generation failed")
|
||||
await message.reply("Бля, кент, чёт движок заглох. Попробуй позже.")
|
||||
|
|
@ -5,4 +5,5 @@ COPY requirements.txt ./
|
|||
RUN pip install --no-cache-dir -r requirements.txt
|
||||
COPY main.py impact.ttf ./
|
||||
COPY maket ./maket
|
||||
COPY AI ./AI
|
||||
CMD ["python", "main.py"]
|
||||
|
|
|
|||
18
README.md
18
README.md
|
|
@ -3,7 +3,8 @@
|
|||
Телеграм‑бот, который:
|
||||
- вписывает текст шрифтом Impact (белый с чёрной обводкой) в макеты (`/maket`);
|
||||
- делает мемы из присланных фото (`/gen_mem Up:... Down:...`);
|
||||
- подсказывает, сколько минут до перекура по заданному расписанию (`/break`).
|
||||
- подсказывает, сколько минут до перекура по заданному расписанию (`/break`);
|
||||
- умеет базарить (`/talk`).
|
||||
|
||||
## Запуск
|
||||
|
||||
|
|
@ -11,7 +12,8 @@
|
|||
docker-compose up --build
|
||||
```
|
||||
|
||||
Переменные окружения: задайте `BOT_TOKEN` в `.env` (уже добавлен шаблон).
|
||||
Переменные окружения: задайте `BOT_TOKEN` в `.env` (уже добавлен шаблон);
|
||||
`LLAMA_API_URL` — адрес llama.cpp сервера (для `/talk`)
|
||||
|
||||
## Команды
|
||||
|
||||
|
|
@ -20,6 +22,7 @@ docker-compose up --build
|
|||
- `/maket <id> <текст>` — вписать текст в макет. Доступные: `1 — Владимир Владимирович Путин самый лучший президент` (737x414, точка 104,240).
|
||||
- `/gen_mem Up:верх Down:низ` — прислать с фото (подпись) или ответом на фото. Можно указывать только Up или только Down.
|
||||
- `/break` — показывает, сколько минут до ближайшего перекура/идёт ли перекур.
|
||||
- `/talk <сообщение>` — побазарить с ИИ. Работает через llama.cpp сервер.
|
||||
|
||||
## Макеты и шрифт
|
||||
|
||||
|
|
@ -29,6 +32,7 @@ docker-compose up --build
|
|||
## Технологии
|
||||
|
||||
- Python 3.11, aiogram 3, Pillow.
|
||||
- llama.cpp для ИИ-фич.
|
||||
- Dockerfile + docker-compose для развёртывания.
|
||||
|
||||
## Кратко о логике
|
||||
|
|
@ -36,3 +40,13 @@ docker-compose up --build
|
|||
- `/maket` подбирает размер текста, чтобы уместить его в область макета.
|
||||
- `/gen_mem` центрирует текст по ширине, масштабирует под изображение, рисует белый текст с чёрной обводкой.
|
||||
- `/break` считает по расписанию в `main.py` (словарь `SCHEDULE`).
|
||||
- `/talk` шлёт запрос к llama.cpp серверу (`LLAMA_API_URL`), системный промпт задаёт стиль фени(Можно изменить).
|
||||
|
||||
## ИИ-модуль (`AI/talk_handler.py`)
|
||||
|
||||
Отдельный модуль, не трогает основной код. Подключается к llama.cpp серверу через OpenAI-совместимый API (`/v1/chat/completions`).
|
||||
|
||||
Настройка сервера:
|
||||
1. Скачать модель: `wget https://huggingface.co/Qwen/Qwen2.5-1.5B-Instruct-GGUF/resolve/main/qwen2.5-1.5b-instruct-q4_k_m.gguf`
|
||||
2. Запустить: `./llama-server -m model.gguf --host 0.0.0.0 --port 8080 -c 2048`
|
||||
3. Прописать URL в `.env`: `LLAMA_API_URL=http://адрес:8080`
|
||||
|
|
|
|||
4
main.py
4
main.py
|
|
@ -1,4 +1,5 @@
|
|||
import asyncio
|
||||
from AI.talk_handler import handle_talk
|
||||
import logging
|
||||
import os
|
||||
import re
|
||||
|
|
@ -132,6 +133,7 @@ HINT_TEXT = (
|
|||
f"• /maket «id» «текст» — доступные: {templates_list_text()} (1: 737x414, точка 104,240).\n"
|
||||
"• /gen_mem Up:верх Down:низ — отправь с фото (как подпись) или ответом на фото.\n"
|
||||
"• /break — узнать, сколько минут до перекура.\n"
|
||||
"• /talk [сообщение] — побазарить с ИИ на фене.\n"
|
||||
"• /prices — старый способ цен.\n"
|
||||
"• /fetch — свежие цены с x-server.\n"
|
||||
"Подсказка: в группах команда должна быть слитно с ботом (/gen_mem@username)."
|
||||
|
|
@ -577,6 +579,7 @@ def main():
|
|||
BotCommand(command="prices", description="цены x-server"),
|
||||
BotCommand(command="fetch", description="цены (новый источник)"),
|
||||
BotCommand(command="zvetok", description="Цветянский бля"),
|
||||
BotCommand(command="talk", description="Побазарить"),
|
||||
]
|
||||
)
|
||||
except Exception: # noqa: WPS440
|
||||
|
|
@ -592,6 +595,7 @@ def main():
|
|||
dp.message.register(handle_prices, Command("prices"))
|
||||
dp.message.register(handle_fetch, Command("fetch"))
|
||||
dp.message.register(handle_zvetok, Command("zvetok"))
|
||||
dp.message.register(handle_talk, Command("talk"))
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
aiogram==3.4.1
|
||||
pillow==10.3.0
|
||||
requests==2.31.0
|
||||
aiogram>=3.4.1
|
||||
pillow>=10.3.0
|
||||
requests>=2.31.0
|
||||
|
|
|
|||
Loading…
Reference in a new issue