forked from zovos/bot_tg
Добавлен казик и гадание
This commit is contained in:
parent
fc85149345
commit
189188db6d
5 changed files with 232 additions and 0 deletions
|
|
@ -18,6 +18,9 @@ PENIS_COOLDOWN = timedelta(hours=24)
|
||||||
PENIS_COOLDOWN_SECONDS = int(PENIS_COOLDOWN.total_seconds())
|
PENIS_COOLDOWN_SECONDS = int(PENIS_COOLDOWN.total_seconds())
|
||||||
PENIS_TOP_LIMIT = 10
|
PENIS_TOP_LIMIT = 10
|
||||||
|
|
||||||
|
# --- Казино ---
|
||||||
|
CASINO_MAX_BET = 1.0
|
||||||
|
|
||||||
# --- Конфигурация макетов ---
|
# --- Конфигурация макетов ---
|
||||||
TEMPLATES = {
|
TEMPLATES = {
|
||||||
"1": {
|
"1": {
|
||||||
|
|
@ -97,6 +100,8 @@ def get_hint_text(templates_str: str) -> str:
|
||||||
"• /penis — миниигра 1 раз в 24 часа.\n"
|
"• /penis — миниигра 1 раз в 24 часа.\n"
|
||||||
"• /top_penis — топ по длине.\n"
|
"• /top_penis — топ по длине.\n"
|
||||||
"• /talk [текст] — ИИ базарит на фене.\n"
|
"• /talk [текст] — ИИ базарит на фене.\n"
|
||||||
|
"• /penis_casino [ставка] — казино на размер.\n"
|
||||||
|
"• /gadanie [тема] — гадание на фене с мемом.\n"
|
||||||
"• /ebalnik — включить/выключить автоответы в чате.\n"
|
"• /ebalnik — включить/выключить автоответы в чате.\n"
|
||||||
"• /prices /fetch — цены.\n"
|
"• /prices /fetch — цены.\n"
|
||||||
"Подсказка: в группах пиши /команда@username_бота."
|
"Подсказка: в группах пиши /команда@username_бота."
|
||||||
|
|
|
||||||
0
games/__init__.py
Normal file
0
games/__init__.py
Normal file
127
games/casino.py
Normal file
127
games/casino.py
Normal file
|
|
@ -0,0 +1,127 @@
|
||||||
|
import random
|
||||||
|
import sqlite3
|
||||||
|
import logging
|
||||||
|
|
||||||
|
import config
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
# Символы слотов и их веса (чем выше вес — тем чаще выпадает)
|
||||||
|
SLOT_SYMBOLS = ["🍒", "🍋", "🔔", "💎", "7️⃣", "🍀"]
|
||||||
|
SLOT_WEIGHTS = [30, 25, 20, 12, 8, 5] # 🍒 чаще всего, 🍀 реже всего
|
||||||
|
|
||||||
|
# Множители для выигрышей: {кол-во совпадений: (мин, макс)}
|
||||||
|
MULTIPLIERS = {
|
||||||
|
2: (1.2, 1.8), # две одинаковых
|
||||||
|
3: (2.0, 3.0), # три одинаковых (джекпот, редко)
|
||||||
|
}
|
||||||
|
|
||||||
|
CASINO_WIN_CHANCE = 0.35
|
||||||
|
|
||||||
|
|
||||||
|
def spin_slots() -> tuple[list[str], int]:
|
||||||
|
reels = random.choices(SLOT_SYMBOLS, weights=SLOT_WEIGHTS, k=3)
|
||||||
|
|
||||||
|
if random.random() > CASINO_WIN_CHANCE:
|
||||||
|
while len(set(reels)) < 3:
|
||||||
|
reels = random.choices(SLOT_SYMBOLS, weights=SLOT_WEIGHTS, k=3)
|
||||||
|
return reels, 0
|
||||||
|
|
||||||
|
counts = {}
|
||||||
|
for s in reels:
|
||||||
|
counts[s] = counts.get(s, 0) + 1
|
||||||
|
max_match = max(counts.values())
|
||||||
|
return reels, max_match
|
||||||
|
|
||||||
|
|
||||||
|
def get_user_length(user_id: int) -> float | None:
|
||||||
|
try:
|
||||||
|
with sqlite3.connect(config.PENIS_DB_PATH) as conn:
|
||||||
|
row = conn.execute(
|
||||||
|
"SELECT length FROM penis_stats WHERE user_id = ?",
|
||||||
|
(user_id,),
|
||||||
|
).fetchone()
|
||||||
|
return float(row[0]) if row else None
|
||||||
|
except sqlite3.Error:
|
||||||
|
logger.exception("casino: failed to get length")
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def update_user_length(user_id: int, delta: float) -> float | None:
|
||||||
|
try:
|
||||||
|
with sqlite3.connect(config.PENIS_DB_PATH) as conn:
|
||||||
|
row = conn.execute(
|
||||||
|
"SELECT length FROM penis_stats WHERE user_id = ?",
|
||||||
|
(user_id,),
|
||||||
|
).fetchone()
|
||||||
|
if row is None:
|
||||||
|
return None
|
||||||
|
new_length = round(float(row[0]) + delta, 1)
|
||||||
|
conn.execute(
|
||||||
|
"UPDATE penis_stats SET length = ? WHERE user_id = ?",
|
||||||
|
(new_length, user_id),
|
||||||
|
)
|
||||||
|
conn.commit()
|
||||||
|
return new_length
|
||||||
|
except sqlite3.Error:
|
||||||
|
logger.exception("casino: failed to update length")
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def play_casino(user_id: int, bet: float) -> str:
|
||||||
|
current = get_user_length(user_id)
|
||||||
|
if current is None:
|
||||||
|
return "Ты ещё ни разу не крутил /penis, братуха. Сначала заведи счёт."
|
||||||
|
|
||||||
|
# Проверяем отрицательный баланс
|
||||||
|
if current <= 0:
|
||||||
|
return "🚫 С кредитом в казино не впускают, братуха. Иди наращивай через /penis."
|
||||||
|
|
||||||
|
# Проверяем максимум ставки
|
||||||
|
if bet > config.CASINO_MAX_BET:
|
||||||
|
return f"Максимальная ставка — {config.CASINO_MAX_BET} см, не жадничай."
|
||||||
|
|
||||||
|
if bet <= 0:
|
||||||
|
return "Ставка должна быть больше нуля, фраер."
|
||||||
|
|
||||||
|
# Проверяем хватает ли
|
||||||
|
if bet > current:
|
||||||
|
return f"У тебя {current:.1f} см, а ставишь {bet:.1f}. Столько нет, фраер."
|
||||||
|
|
||||||
|
# Крутим
|
||||||
|
reels, matches = spin_slots()
|
||||||
|
slots_display = " | ".join(reels)
|
||||||
|
|
||||||
|
if matches >= 2:
|
||||||
|
min_mult, max_mult = MULTIPLIERS[matches]
|
||||||
|
multiplier = round(random.uniform(min_mult, max_mult), 1)
|
||||||
|
winnings = round(bet * multiplier, 1)
|
||||||
|
new_length = update_user_length(user_id, winnings)
|
||||||
|
if new_length is None:
|
||||||
|
return "Ошибка БД. Попробуй позже."
|
||||||
|
|
||||||
|
if matches == 3:
|
||||||
|
return (
|
||||||
|
f"🎰 {slots_display}\n\n"
|
||||||
|
f"🎉 ДЖЕКПОТ!!! Три одинаковых!\n"
|
||||||
|
f"Множитель: x{multiplier}\n"
|
||||||
|
f"Выигрыш: +{winnings:.1f} см\n"
|
||||||
|
f"📏 Текущий размер: {new_length:.1f} см"
|
||||||
|
)
|
||||||
|
return (
|
||||||
|
f"🎰 {slots_display}\n\n"
|
||||||
|
f"✅ Выигрыш! Две совпали.\n"
|
||||||
|
f"Множитель: x{multiplier}\n"
|
||||||
|
f"Выигрыш: +{winnings:.1f} см\n"
|
||||||
|
f"📏 Текущий размер: {new_length:.1f} см"
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
new_length = update_user_length(user_id, -bet)
|
||||||
|
if new_length is None:
|
||||||
|
return "Ошибка БД. Попробуй позже."
|
||||||
|
return (
|
||||||
|
f"🎰 {slots_display}\n\n"
|
||||||
|
f"❌ Мимо, братуха.\n"
|
||||||
|
f"Проигрыш: -{bet:.1f} см\n"
|
||||||
|
f"📏 Текущий размер: {new_length:.1f} см"
|
||||||
|
)
|
||||||
57
games/fortune.py
Normal file
57
games/fortune.py
Normal file
|
|
@ -0,0 +1,57 @@
|
||||||
|
import logging
|
||||||
|
import aiohttp
|
||||||
|
import os
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
LLAMA_API_URL = os.getenv("LLAMA_API_URL", "https://mirror.porno4free.ru/zovos-ai/")
|
||||||
|
|
||||||
|
FORTUNE_PROMPT = (
|
||||||
|
"Ты — уличный гадатель с района, базаришь на фене. "
|
||||||
|
"Пользователь хочет погадать на тему. Выдай шуточное гадание, "
|
||||||
|
"коротко и дерзко, 2-3 предложения максимум. "
|
||||||
|
"В конце ОБЯЗАТЕЛЬНО напиши на отдельной строке в формате:\n"
|
||||||
|
"MEM_UP: <текст сверху для мема, максимум 4 слова>\n"
|
||||||
|
"MEM_DOWN: <текст снизу для мема, максимум 4 слова>\n"
|
||||||
|
"Тексты для мема должны быть смешными и короткими."
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def generate_fortune(topic: str) -> tuple[str, str, str]:
|
||||||
|
url = f"{LLAMA_API_URL.rstrip('/')}/v1/chat/completions"
|
||||||
|
payload = {
|
||||||
|
"messages": [
|
||||||
|
{"role": "system", "content": FORTUNE_PROMPT},
|
||||||
|
{"role": "user", "content": f"Погадай мне на: {topic}"},
|
||||||
|
],
|
||||||
|
"max_tokens": 200,
|
||||||
|
"temperature": 0.9,
|
||||||
|
"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()
|
||||||
|
text = data["choices"][0]["message"]["content"].strip()
|
||||||
|
|
||||||
|
up_text = ""
|
||||||
|
down_text = ""
|
||||||
|
fortune_lines = []
|
||||||
|
|
||||||
|
for line in text.split("\n"):
|
||||||
|
stripped = line.strip()
|
||||||
|
if stripped.upper().startswith("MEM_UP:"):
|
||||||
|
up_text = stripped.split(":", 1)[1].strip()
|
||||||
|
elif stripped.upper().startswith("MEM_DOWN:"):
|
||||||
|
down_text = stripped.split(":", 1)[1].strip()
|
||||||
|
else:
|
||||||
|
fortune_lines.append(line)
|
||||||
|
|
||||||
|
fortune_text = "\n".join(fortune_lines).strip()
|
||||||
|
|
||||||
|
if not up_text:
|
||||||
|
up_text = "Гадание"
|
||||||
|
if not down_text:
|
||||||
|
down_text = "по фене"
|
||||||
|
|
||||||
|
return fortune_text, up_text, down_text
|
||||||
43
main.py
43
main.py
|
|
@ -19,6 +19,8 @@ from aiogram.client.default import DefaultBotProperties
|
||||||
|
|
||||||
# Локальные импорты
|
# Локальные импорты
|
||||||
from AI.talk_handler import handle_talk, generate_autoreply, push_message
|
from AI.talk_handler import handle_talk, generate_autoreply, push_message
|
||||||
|
from games.casino import play_casino
|
||||||
|
from games.fortune import generate_fortune
|
||||||
from zparser import get_military_data
|
from zparser import get_military_data
|
||||||
import config
|
import config
|
||||||
|
|
||||||
|
|
@ -711,6 +713,43 @@ async def handle_keywords(message: Message):
|
||||||
except Exception:
|
except Exception:
|
||||||
logger.exception("Autoreply failed")
|
logger.exception("Autoreply failed")
|
||||||
|
|
||||||
|
async def handle_penis_casino_cmd(message: Message):
|
||||||
|
if not message.from_user:
|
||||||
|
await message.answer("Команда доступна только пользователям.")
|
||||||
|
return
|
||||||
|
parts = message.text.split(maxsplit=1)
|
||||||
|
if len(parts) < 2:
|
||||||
|
await message.reply("Формат: /penis_casino <ставка>\nПример: /penis_casino 0.5")
|
||||||
|
return
|
||||||
|
try:
|
||||||
|
bet = round(float(parts[1].replace(",", ".")), 1)
|
||||||
|
except ValueError:
|
||||||
|
await message.reply("Ставка должна быть числом, фраер. Пример: /penis_casino 0.5")
|
||||||
|
return
|
||||||
|
result = await asyncio.to_thread(play_casino, message.from_user.id, bet)
|
||||||
|
await message.reply(result, parse_mode=None)
|
||||||
|
|
||||||
|
async def handle_gadanie_cmd(message: Message):
|
||||||
|
parts = message.text.split(maxsplit=1)
|
||||||
|
if len(parts) < 2 or not parts[1].strip():
|
||||||
|
await message.reply("Напиши на что гадать, кент. Пример: /gadanie любовь")
|
||||||
|
return
|
||||||
|
topic = parts[1].strip()
|
||||||
|
await message.bot.send_chat_action(chat_id=message.chat.id, action="typing")
|
||||||
|
try:
|
||||||
|
fortune_text, up_text, down_text = await generate_fortune(topic)
|
||||||
|
await message.reply(f"🔮 Гадание на «{topic}»:\n\n{fortune_text}", parse_mode=None)
|
||||||
|
photo_path = config.BASE_DIR / "maket" / "vape.jpg"
|
||||||
|
if photo_path.exists():
|
||||||
|
img = Image.open(photo_path).convert("RGB")
|
||||||
|
meme_output = await asyncio.to_thread(generate_meme, img, up_text, down_text)
|
||||||
|
await message.reply_photo(
|
||||||
|
BufferedInputFile(meme_output.getvalue(), filename="gadanie.jpg")
|
||||||
|
)
|
||||||
|
except Exception:
|
||||||
|
logger.exception("Gadanie failed")
|
||||||
|
await message.reply("Бля, карты рассыпались, попробуй позже.")
|
||||||
|
|
||||||
# --- Запуск ---
|
# --- Запуск ---
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
|
|
@ -735,6 +774,8 @@ def main():
|
||||||
dp.message.register(handle_talk, Command("talk"))
|
dp.message.register(handle_talk, Command("talk"))
|
||||||
dp.message.register(handle_svodka_cmd, Command("svodka"))
|
dp.message.register(handle_svodka_cmd, Command("svodka"))
|
||||||
dp.message.register(handle_ebalnik_cmd, Command("ebalnik"))
|
dp.message.register(handle_ebalnik_cmd, Command("ebalnik"))
|
||||||
|
dp.message.register(handle_penis_casino_cmd, Command("penis_casino"))
|
||||||
|
dp.message.register(handle_gadanie_cmd, Command("gadanie"))
|
||||||
dp.message.register(handle_gen_mem, Command("gen_mem"))
|
dp.message.register(handle_gen_mem, Command("gen_mem"))
|
||||||
dp.message.register(handle_keywords, F.text)
|
dp.message.register(handle_keywords, F.text)
|
||||||
|
|
||||||
|
|
@ -754,6 +795,8 @@ def main():
|
||||||
BotCommand(command="zvetok", description="Цветянский бля"),
|
BotCommand(command="zvetok", description="Цветянский бля"),
|
||||||
BotCommand(command="talk", description="Побазарить"),
|
BotCommand(command="talk", description="Побазарить"),
|
||||||
BotCommand(command="ebalnik", description="вкл/выкл автответов"),
|
BotCommand(command="ebalnik", description="вкл/выкл автответов"),
|
||||||
|
BotCommand(command="penis_casino", description="казино на размер"),
|
||||||
|
BotCommand(command="gadanie", description="гадание на фене"),
|
||||||
BotCommand(command="svodka", description="СВО: итоги"),
|
BotCommand(command="svodka", description="СВО: итоги"),
|
||||||
]
|
]
|
||||||
scopes = (
|
scopes = (
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue