Kenzie-bot / main.py
MashaKenzie's picture
Update main.py
ed8b1e5 verified
import os
import vk_api
from vk_api.longpoll import VkLongPoll, VkEventType
from vk_api.upload import VkUpload
from huggingface_hub import InferenceClient
import io
import re
import time
from collections import deque
from threading import Thread
# ─── Настройки ────────────────────────────────────────────────────────────────
try:
VK_TOKEN = os.environ["VK_TOKEN"]
HF_TOKEN = os.environ["HF_TOKEN"] # Обязательно зарегистрированный токен HF
BOT_ID = int(os.environ["BOT_ID"])
except KeyError as e:
print(f"ОШИБКА: Нет ключа {e} в переменных окружения / secrets")
exit(1)
BOT_NAMES = ["бот", "эй", "кензи", "ии", "kenzie", "кензи", "@kenzie"]
DRAW_COMMAND = "!картинка"
# Стабильные модели на февраль 2026 (бесплатный Inference API)
LLM_MODEL = "Qwen/Qwen2.5-7B-Instruct" # или "mistralai/Mistral-7B-Instruct-v0.3"
IMAGE_MODEL = "stabilityai/stable-diffusion-xl-base-1.0" # Самая надёжная бесплатная SDXL на HF Inference
# Инициализация
client = InferenceClient(token=HF_TOKEN.strip())
vk_session = vk_api.VkApi(token=VK_TOKEN)
vk = vk_session.get_api()
longpoll = VkLongPoll(vk_session)
upload = VkUpload(vk_session)
chat_memory = {} # {peer_id: deque([...]) }
def send_msg(peer_id, text, reply_to=None, attach=None):
try:
params = {
"peer_id": peer_id,
"message": text,
"random_id": 0
}
if reply_to:
params["reply_to"] = reply_to
if attach:
params["attachment"] = attach
vk_session.method("messages.send", params)
except Exception as e:
print(f"VK send error → {e}")
def ask_ai(messages: list):
try:
completion = client.chat.completions.create(
model=LLM_MODEL,
messages=messages,
max_tokens=600,
temperature=0.75,
stream=False
)
return completion.choices[0].message.content.strip()
except Exception as e:
print(f"LLM error: {e}")
return (
"🤖 Что-то пошло не так… Возможно, лимит бесплатных запросов.\n"
"Попробуй позже или спроси попроще."
)
def generate_image(prompt: str):
try:
print(f"Генерация: {prompt} (модель: {IMAGE_MODEL})")
# SDXL обычно лучше работает с 30–50 шагами, но на бесплатном API лимит скорости → ставим меньше
image = client.text_to_image(
prompt = prompt,
model = IMAGE_MODEL,
num_inference_steps = 28, # 20–40 — хороший компромисс скорость/качество
guidance_scale = 7.5, # классическое значение для SDXL
negative_prompt = "blurry, low quality, deformed, ugly, bad anatomy" # опционально
)
img_bytes = io.BytesIO()
image.save(img_bytes, format="PNG")
img_bytes.seek(0)
return img_bytes.read(), None
except Exception as e:
err_str = str(e)
print(f"Image error: {err_str}")
if "Pillow" in err_str:
return None, "Установи Pillow → pip install pillow"
if "429" in err_str or "rate limit" in err_str.lower():
return None, "Лимит бесплатных запросов HF. Подожди 1–5 минут."
return None, err_str[:180]
def handle_draw(peer_id, msg_id, text):
prompt = text.replace(DRAW_COMMAND, "", 1).strip()
if not prompt:
send_msg(peer_id, "Что нарисовать?\nПример: !картинка cyberpunk city at night neon lights", reply_to=msg_id)
return
send_msg(peer_id, f"🎨 Генерирую (SDXL base, ~20–35 сек)...\n«{prompt}»", reply_to=msg_id)
img_data, err = generate_image(prompt)
if img_data:
try:
file_obj = io.BytesIO(img_data)
file_obj.name = "generated.png"
photos = upload.photo_messages(photos=file_obj)
photo = photos[0]
attach = f"photo{photo['owner_id']}_{photo['id']}"
send_msg(peer_id, "✨ Готово!", reply_to=msg_id, attach=attach)
except Exception as e:
print(f"VK upload fail: {e}")
send_msg(peer_id, "❌ Ошибка загрузки в ВК", reply_to=msg_id)
else:
send_msg(peer_id, f"❌ Не получилось\n{err}", reply_to=msg_id)
def process_message(event):
if not event.text:
return
text = event.text.strip()
low = text.lower()
# Рисование
if low.startswith(DRAW_COMMAND):
Thread(target=handle_draw, args=(event.peer_id, event.message_id, text)).start()
return
# Обращение к боту
mentioned = any(word in low for word in BOT_NAMES) or f"[club{BOT_ID}|" in text
if not mentioned:
return
clean = text
for name in BOT_NAMES:
clean = re.sub(rf'\b{name}\b', '', clean, flags=re.IGNORECASE)
clean = re.sub(r'\[club\d+\|[^\]]+\]', '', clean)
clean = clean.strip(" ,.!?*").strip()
if not clean:
send_msg(event.peer_id, "Привет! Чем помочь? 😊", reply_to=event.message_id)
return
if event.peer_id not in chat_memory:
chat_memory[event.peer_id] = deque(maxlen=6)
chat_memory[event.peer_id].append({"role": "user", "content": clean})
answer = ask_ai(list(chat_memory[event.peer_id]))
chat_memory[event.peer_id].append({"role": "assistant", "content": answer})
send_msg(event.peer_id, answer, reply_to=event.message_id)
if __name__ == "__main__":
print("Бот запущен")
print(f"LLM : {LLM_MODEL}")
print(f"Image : {IMAGE_MODEL}")
print("Ctrl+C → выход\n")
while True:
try:
for event in longpoll.listen():
if event.type == VkEventType.MESSAGE_NEW and event.to_me:
process_message(event)
except KeyboardInterrupt:
print("\nОстановка...")
break
except Exception as e:
print(f"Критическая ошибка → {e}\nПерезапуск через 8 сек...")
time.sleep(8)