Compare commits
1 commit
9f6a7464d1
...
189188db6d
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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_TOP_LIMIT = 10
|
||||
|
||||
# --- Казино ---
|
||||
CASINO_MAX_BET = 1.0
|
||||
|
||||
# --- Конфигурация макетов ---
|
||||
TEMPLATES = {
|
||||
"1": {
|
||||
|
|
@ -97,6 +100,8 @@ def get_hint_text(templates_str: str) -> str:
|
|||
"• /penis — миниигра 1 раз в 24 часа.\n"
|
||||
"• /top_penis — топ по длине.\n"
|
||||
"• /talk [текст] — ИИ базарит на фене.\n"
|
||||
"• /penis_casino [ставка] — казино на размер.\n"
|
||||
"• /gadanie [тема] — гадание на фене с мемом.\n"
|
||||
"• /ebalnik — включить/выключить автоответы в чате.\n"
|
||||
"• /prices /fetch — цены.\n"
|
||||
"Подсказка: в группах пиши /команда@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 games.casino import play_casino
|
||||
from games.fortune import generate_fortune
|
||||
from zparser import get_military_data
|
||||
import config
|
||||
|
||||
|
|
@ -711,6 +713,43 @@ async def handle_keywords(message: Message):
|
|||
except Exception:
|
||||
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():
|
||||
|
|
@ -735,6 +774,8 @@ def main():
|
|||
dp.message.register(handle_talk, Command("talk"))
|
||||
dp.message.register(handle_svodka_cmd, Command("svodka"))
|
||||
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_keywords, F.text)
|
||||
|
||||
|
|
@ -754,6 +795,8 @@ def main():
|
|||
BotCommand(command="zvetok", description="Цветянский бля"),
|
||||
BotCommand(command="talk", description="Побазарить"),
|
||||
BotCommand(command="ebalnik", description="вкл/выкл автответов"),
|
||||
BotCommand(command="penis_casino", description="казино на размер"),
|
||||
BotCommand(command="gadanie", description="гадание на фене"),
|
||||
BotCommand(command="svodka", description="СВО: итоги"),
|
||||
]
|
||||
scopes = (
|
||||
|
|
|
|||
Loading…
Reference in a new issue