Compare commits
No commits in common. "7289296169dd5aeff25565d0d02cc859320ff94d" and "a7c047f8472229da990c972f1cf53956d892ad4b" have entirely different histories.
7289296169
...
a7c047f847
5 changed files with 1 additions and 405 deletions
1
.env
1
.env
|
|
@ -1,3 +1,2 @@
|
||||||
BOT_TOKEN=8488665890:AAH7FY5I2xzHhfOdxlzb11ewl2I-MMTH2-o
|
BOT_TOKEN=8488665890:AAH7FY5I2xzHhfOdxlzb11ewl2I-MMTH2-o
|
||||||
TZ=Europe/Moscow
|
TZ=Europe/Moscow
|
||||||
ODDS_API_KEY=c29bc26dee8aa9d95a5ce8d14ea63923
|
|
||||||
|
|
|
||||||
|
|
@ -9,8 +9,7 @@
|
||||||
- авто-ответы в чате (10% шанс + триггер-слова);
|
- авто-ответы в чате (10% шанс + триггер-слова);
|
||||||
- миниигра `/penis` + `/top_penis`;
|
- миниигра `/penis` + `/top_penis`;
|
||||||
- казино на размер (`/penis_casino`);
|
- казино на размер (`/penis_casino`);
|
||||||
- гадание на фене с мемом (`/gadanie`);
|
- гадание на фене с мемом (`/gadanie`).
|
||||||
- ставки на матчи CS2, Dota 2, футбол с реальными коэффициентами (`/matches`, `/bet`).
|
|
||||||
|
|
||||||
## Запуск
|
## Запуск
|
||||||
|
|
||||||
|
|
@ -32,13 +31,11 @@ docker compose down
|
||||||
|
|
||||||
Переменные окружения: задайте `BOT_TOKEN` в `.env`.
|
Переменные окружения: задайте `BOT_TOKEN` в `.env`.
|
||||||
Для ИИ-фич нужен `LLAMA_API_URL`.
|
Для ИИ-фич нужен `LLAMA_API_URL`.
|
||||||
Для ставок нужен `ODDS_API_KEY` (бесплатно на https://the-odds-api.com).
|
|
||||||
|
|
||||||
Контейнер хранит состояние в `./db`:
|
Контейнер хранит состояние в `./db`:
|
||||||
- `penis_stats.sqlite3`
|
- `penis_stats.sqlite3`
|
||||||
- `chat_history.sqlite3`
|
- `chat_history.sqlite3`
|
||||||
- `polychaetsi_stats.json`
|
- `polychaetsi_stats.json`
|
||||||
- `bets.sqlite3`
|
|
||||||
|
|
||||||
## Команды
|
## Команды
|
||||||
|
|
||||||
|
|
@ -55,9 +52,6 @@ docker compose down
|
||||||
- `/penis_casino <ставка>` — казино на размер (макс 1 см, казино всегда в выигрыше).
|
- `/penis_casino <ставка>` — казино на размер (макс 1 см, казино всегда в выигрыше).
|
||||||
- `/gadanie <тема>` — ИИ-гадание на фене + авто-мем.
|
- `/gadanie <тема>` — ИИ-гадание на фене + авто-мем.
|
||||||
- `/ebalnik` — вкл/выкл авто-ответы бота в чате.
|
- `/ebalnik` — вкл/выкл авто-ответы бота в чате.
|
||||||
- `/matches [cs|dota|football]` — ближайшие матчи с коэффициентами.
|
|
||||||
- `/bet <спорт> <номер> <команда> <ставка>` — поставить см на матч.
|
|
||||||
- `/mybets` — мои активные ставки.
|
|
||||||
- `/svodka` — сводка СВО.
|
- `/svodka` — сводка СВО.
|
||||||
|
|
||||||
## Макеты и шрифт
|
## Макеты и шрифт
|
||||||
|
|
@ -86,7 +80,6 @@ docker compose down
|
||||||
- `/talk` шлёт запрос к llama.cpp серверу с контекстом 100 сообщений (хранятся в SQLite).
|
- `/talk` шлёт запрос к llama.cpp серверу с контекстом 100 сообщений (хранятся в SQLite).
|
||||||
- `/penis_casino` — слоты с множителем x1.2–x3 (`games/casino.py`).
|
- `/penis_casino` — слоты с множителем x1.2–x3 (`games/casino.py`).
|
||||||
- `/gadanie` — ИИ-гадание + авто-мем через `generate_meme` (`games/fortune.py`).
|
- `/gadanie` — ИИ-гадание + авто-мем через `generate_meme` (`games/fortune.py`).
|
||||||
- `/matches` + `/bet` — ставки на реальные матчи, коэффициенты через The-Odds-API (`games/betting.py`).
|
|
||||||
|
|
||||||
## ИИ-модуль (`AI/talk_handler.py`)
|
## ИИ-модуль (`AI/talk_handler.py`)
|
||||||
|
|
||||||
|
|
|
||||||
15
config.py
15
config.py
|
|
@ -27,18 +27,6 @@ POLYCHAETSI_IMAGE_PATH = BASE_DIR / "maket" / "cvetok.jpg"
|
||||||
# --- Казино ---
|
# --- Казино ---
|
||||||
CASINO_MAX_BET = 1.0
|
CASINO_MAX_BET = 1.0
|
||||||
|
|
||||||
# --- Ставки на матчи ---
|
|
||||||
ODDS_API_KEY = os.getenv("ODDS_API_KEY", "")
|
|
||||||
ODDS_API_BASE = "https://api.the-odds-api.com/v4"
|
|
||||||
ODDS_SPORTS = {
|
|
||||||
"cs": "esports_csgo",
|
|
||||||
"dota": "esports_dota2",
|
|
||||||
"football": "soccer_epl",
|
|
||||||
}
|
|
||||||
BET_MAX_AMOUNT = 3.0
|
|
||||||
BET_DB_PATH = "/db/bets.sqlite3"
|
|
||||||
ODDS_CACHE_TTL = 900
|
|
||||||
|
|
||||||
# --- Magnit API ---
|
# --- Magnit API ---
|
||||||
MAGNIT_API_BASE_URL = os.getenv("MAGNIT_API_BASE_URL", "https://mirror.porno4free.ru/magnit")
|
MAGNIT_API_BASE_URL = os.getenv("MAGNIT_API_BASE_URL", "https://mirror.porno4free.ru/magnit")
|
||||||
MAGNIT_STORE_CODE = os.getenv("MAGNIT_STORE_CODE", "618224")
|
MAGNIT_STORE_CODE = os.getenv("MAGNIT_STORE_CODE", "618224")
|
||||||
|
|
@ -205,9 +193,6 @@ def get_hint_text(templates_str: str) -> str:
|
||||||
"• /talk [текст] — ИИ базарит на фене.\n"
|
"• /talk [текст] — ИИ базарит на фене.\n"
|
||||||
"• /penis_casino [ставка] — казино на размер.\n"
|
"• /penis_casino [ставка] — казино на размер.\n"
|
||||||
"• /gadanie [тема] — гадание на фене с мемом.\n"
|
"• /gadanie [тема] — гадание на фене с мемом.\n"
|
||||||
"• /matches [cs|dota|football] — матчи для ставок.\n"
|
|
||||||
"• /bet <спорт> <номер> <команда> <ставка> — поставить см.\n"
|
|
||||||
"• /mybets — мои ставки.\n"
|
|
||||||
"• /ebalnik — включить/выключить автоответы в чате.\n"
|
"• /ebalnik — включить/выключить автоответы в чате.\n"
|
||||||
"• /polychaetsi — топ по слову «получается».\n"
|
"• /polychaetsi — топ по слову «получается».\n"
|
||||||
"• /prices /fetch — цены и остатки Магнита.\n"
|
"• /prices /fetch — цены и остатки Магнита.\n"
|
||||||
|
|
|
||||||
308
games/betting.py
308
games/betting.py
|
|
@ -1,308 +0,0 @@
|
||||||
import time as _time
|
|
||||||
import sqlite3
|
|
||||||
import logging
|
|
||||||
from datetime import datetime
|
|
||||||
from pathlib import Path
|
|
||||||
from zoneinfo import ZoneInfo
|
|
||||||
|
|
||||||
import aiohttp
|
|
||||||
|
|
||||||
import config
|
|
||||||
from games.casino import get_user_length, update_user_length
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
_matches_cache: dict[str, tuple[float, list]] = {}
|
|
||||||
|
|
||||||
|
|
||||||
def _init_bets_db() -> None:
|
|
||||||
Path(config.BET_DB_PATH).parent.mkdir(parents=True, exist_ok=True)
|
|
||||||
with sqlite3.connect(config.BET_DB_PATH) as conn:
|
|
||||||
conn.execute(
|
|
||||||
"""
|
|
||||||
CREATE TABLE IF NOT EXISTS bets (
|
|
||||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
||||||
user_id INTEGER NOT NULL,
|
|
||||||
match_id TEXT NOT NULL,
|
|
||||||
sport TEXT NOT NULL,
|
|
||||||
home_team TEXT NOT NULL,
|
|
||||||
away_team TEXT NOT NULL,
|
|
||||||
chosen_team TEXT NOT NULL,
|
|
||||||
amount REAL NOT NULL,
|
|
||||||
odds REAL NOT NULL,
|
|
||||||
status TEXT DEFAULT 'pending',
|
|
||||||
created_ts INTEGER
|
|
||||||
)
|
|
||||||
"""
|
|
||||||
)
|
|
||||||
conn.execute("CREATE INDEX IF NOT EXISTS idx_bets_user ON bets(user_id, status)")
|
|
||||||
conn.execute("CREATE INDEX IF NOT EXISTS idx_bets_match ON bets(match_id, status)")
|
|
||||||
conn.commit()
|
|
||||||
|
|
||||||
|
|
||||||
_init_bets_db()
|
|
||||||
|
|
||||||
|
|
||||||
async def fetch_matches(sport_key: str) -> list[dict]:
|
|
||||||
now = _time.time()
|
|
||||||
cached = _matches_cache.get(sport_key)
|
|
||||||
if cached and (now - cached[0]) < config.ODDS_CACHE_TTL:
|
|
||||||
return cached[1]
|
|
||||||
|
|
||||||
if not config.ODDS_API_KEY:
|
|
||||||
return []
|
|
||||||
|
|
||||||
url = f"{config.ODDS_API_BASE}/sports/{sport_key}/odds"
|
|
||||||
params = {
|
|
||||||
"apiKey": config.ODDS_API_KEY,
|
|
||||||
"regions": "eu",
|
|
||||||
"markets": "h2h",
|
|
||||||
"oddsFormat": "decimal",
|
|
||||||
}
|
|
||||||
|
|
||||||
try:
|
|
||||||
async with aiohttp.ClientSession() as session:
|
|
||||||
async with session.get(url, params=params, timeout=aiohttp.ClientTimeout(total=15)) as resp:
|
|
||||||
if resp.status != 200:
|
|
||||||
logger.warning(f"Odds API returned {resp.status}")
|
|
||||||
return cached[1] if cached else []
|
|
||||||
data = await resp.json()
|
|
||||||
except Exception:
|
|
||||||
logger.exception("Failed to fetch odds")
|
|
||||||
return cached[1] if cached else []
|
|
||||||
|
|
||||||
matches = []
|
|
||||||
for event in data:
|
|
||||||
best_odds = {}
|
|
||||||
for bookmaker in event.get("bookmakers", []):
|
|
||||||
for market in bookmaker.get("markets", []):
|
|
||||||
if market["key"] == "h2h":
|
|
||||||
for outcome in market["outcomes"]:
|
|
||||||
name = outcome["name"]
|
|
||||||
price = outcome["price"]
|
|
||||||
if name not in best_odds or price > best_odds[name]:
|
|
||||||
best_odds[name] = price
|
|
||||||
|
|
||||||
if len(best_odds) >= 2:
|
|
||||||
matches.append({
|
|
||||||
"id": event["id"],
|
|
||||||
"home": event["home_team"],
|
|
||||||
"away": event["away_team"],
|
|
||||||
"commence": event.get("commence_time", ""),
|
|
||||||
"odds": best_odds,
|
|
||||||
})
|
|
||||||
|
|
||||||
_matches_cache[sport_key] = (now, matches)
|
|
||||||
return matches
|
|
||||||
|
|
||||||
|
|
||||||
def format_matches(matches: list[dict], sport_label: str) -> str:
|
|
||||||
if not matches:
|
|
||||||
return f"🏟 {sport_label} — матчей не найдено."
|
|
||||||
|
|
||||||
lines = [f"🏟 {sport_label} — ближайшие матчи:\n"]
|
|
||||||
for i, m in enumerate(matches[:10], 1):
|
|
||||||
odds_parts = []
|
|
||||||
for team, coeff in m["odds"].items():
|
|
||||||
odds_parts.append(f"{team}: {coeff:.2f}")
|
|
||||||
odds_str = " | ".join(odds_parts)
|
|
||||||
|
|
||||||
start_str = ""
|
|
||||||
if m["commence"]:
|
|
||||||
try:
|
|
||||||
dt = datetime.fromisoformat(m["commence"].replace("Z", "+00:00"))
|
|
||||||
msk = dt.astimezone(ZoneInfo(config.DEFAULT_TZ))
|
|
||||||
start_str = f"\n 🕐 {msk.strftime('%d.%m %H:%M')} МСК"
|
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
|
|
||||||
lines.append(f"{i}️⃣ {m['home']} vs {m['away']}\n {odds_str}{start_str}\n")
|
|
||||||
|
|
||||||
lines.append("Ставка: /bet <номер> <команда> <сумма>")
|
|
||||||
return "\n".join(lines)
|
|
||||||
|
|
||||||
|
|
||||||
def _get_sport_label(key: str) -> str:
|
|
||||||
labels = {"cs": "🎮 CS2", "dota": "🎮 Dota 2", "football": "⚽ Футбол"}
|
|
||||||
return labels.get(key, key)
|
|
||||||
|
|
||||||
|
|
||||||
async def get_formatted_matches(sport_alias: str | None) -> str:
|
|
||||||
if sport_alias and sport_alias in config.ODDS_SPORTS:
|
|
||||||
sport_key = config.ODDS_SPORTS[sport_alias]
|
|
||||||
matches = await fetch_matches(sport_key)
|
|
||||||
return format_matches(matches, _get_sport_label(sport_alias))
|
|
||||||
|
|
||||||
parts = []
|
|
||||||
for alias, sport_key in config.ODDS_SPORTS.items():
|
|
||||||
matches = await fetch_matches(sport_key)
|
|
||||||
parts.append(format_matches(matches, _get_sport_label(alias)))
|
|
||||||
return "\n\n".join(parts)
|
|
||||||
|
|
||||||
|
|
||||||
_last_fetched_matches: dict[str, list[dict]] = {}
|
|
||||||
|
|
||||||
|
|
||||||
async def get_match_by_index(sport_alias: str, index: int) -> dict | None:
|
|
||||||
sport_key = config.ODDS_SPORTS.get(sport_alias)
|
|
||||||
if not sport_key:
|
|
||||||
return None
|
|
||||||
matches = await fetch_matches(sport_key)
|
|
||||||
if 0 < index <= len(matches):
|
|
||||||
return matches[index - 1]
|
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
def place_bet(user_id: int, match: dict, sport: str, chosen_team: str, amount: float) -> str:
|
|
||||||
current = get_user_length(user_id)
|
|
||||||
if current is None:
|
|
||||||
return "Сначала заведи счёт через /penis, братуха."
|
|
||||||
|
|
||||||
if current <= 0:
|
|
||||||
return "🚫 С кредитом ставки не принимаем."
|
|
||||||
|
|
||||||
if amount <= 0:
|
|
||||||
return "Ставка должна быть больше нуля."
|
|
||||||
|
|
||||||
if amount > config.BET_MAX_AMOUNT:
|
|
||||||
return f"Максимальная ставка — {config.BET_MAX_AMOUNT} см."
|
|
||||||
|
|
||||||
if amount > current:
|
|
||||||
return f"У тебя {current:.1f} см, а ставишь {amount:.1f}. Не хватает, фраер."
|
|
||||||
|
|
||||||
team_odds = match["odds"].get(chosen_team)
|
|
||||||
if team_odds is None:
|
|
||||||
available = ", ".join(match["odds"].keys())
|
|
||||||
return f"Команда не найдена. Доступные: {available}"
|
|
||||||
|
|
||||||
new_length = update_user_length(user_id, -amount)
|
|
||||||
if new_length is None:
|
|
||||||
return "Ошибка БД."
|
|
||||||
|
|
||||||
now_ts = int(_time.time())
|
|
||||||
try:
|
|
||||||
with sqlite3.connect(config.BET_DB_PATH) as conn:
|
|
||||||
conn.execute(
|
|
||||||
"""INSERT INTO bets (user_id, match_id, sport, home_team, away_team,
|
|
||||||
chosen_team, amount, odds, status, created_ts)
|
|
||||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, 'pending', ?)""",
|
|
||||||
(user_id, match["id"], sport, match["home"], match["away"],
|
|
||||||
chosen_team, amount, team_odds, now_ts),
|
|
||||||
)
|
|
||||||
conn.commit()
|
|
||||||
except sqlite3.Error:
|
|
||||||
logger.exception("Failed to place bet")
|
|
||||||
update_user_length(user_id, amount)
|
|
||||||
return "Ошибка БД."
|
|
||||||
|
|
||||||
potential = round(amount * team_odds, 1)
|
|
||||||
return (
|
|
||||||
f"✅ Ставка принята!\n"
|
|
||||||
f"🏟 {match['home']} vs {match['away']}\n"
|
|
||||||
f"📌 {chosen_team} (x{team_odds:.2f})\n"
|
|
||||||
f"💰 Ставка: {amount:.1f} см\n"
|
|
||||||
f"🎯 Возможный выигрыш: {potential:.1f} см\n"
|
|
||||||
f"📏 Остаток: {new_length:.1f} см"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def get_user_bets(user_id: int) -> str:
|
|
||||||
try:
|
|
||||||
with sqlite3.connect(config.BET_DB_PATH) as conn:
|
|
||||||
conn.row_factory = sqlite3.Row
|
|
||||||
rows = conn.execute(
|
|
||||||
"SELECT * FROM bets WHERE user_id = ? AND status = 'pending' ORDER BY created_ts DESC LIMIT 10",
|
|
||||||
(user_id,),
|
|
||||||
).fetchall()
|
|
||||||
except sqlite3.Error:
|
|
||||||
return "Ошибка БД."
|
|
||||||
|
|
||||||
if not rows:
|
|
||||||
return "У тебя нет активных ставок."
|
|
||||||
|
|
||||||
lines = ["📋 Твои ставки:\n"]
|
|
||||||
for r in rows:
|
|
||||||
potential = round(r["amount"] * r["odds"], 1)
|
|
||||||
lines.append(
|
|
||||||
f"• {r['home_team']} vs {r['away_team']}\n"
|
|
||||||
f" {r['chosen_team']} (x{r['odds']:.2f}), {r['amount']:.1f} см → {potential:.1f} см"
|
|
||||||
)
|
|
||||||
return "\n".join(lines)
|
|
||||||
|
|
||||||
|
|
||||||
async def settle_bets() -> list[str]:
|
|
||||||
notifications = []
|
|
||||||
try:
|
|
||||||
with sqlite3.connect(config.BET_DB_PATH) as conn:
|
|
||||||
conn.row_factory = sqlite3.Row
|
|
||||||
pending = conn.execute(
|
|
||||||
"SELECT DISTINCT match_id, sport FROM bets WHERE status = 'pending'"
|
|
||||||
).fetchall()
|
|
||||||
except sqlite3.Error:
|
|
||||||
return notifications
|
|
||||||
|
|
||||||
for row in pending:
|
|
||||||
match_id = row["match_id"]
|
|
||||||
sport = row["sport"]
|
|
||||||
sport_key = config.ODDS_SPORTS.get(sport, sport)
|
|
||||||
|
|
||||||
try:
|
|
||||||
url = f"{config.ODDS_API_BASE}/sports/{sport_key}/scores"
|
|
||||||
params = {"apiKey": config.ODDS_API_KEY, "daysFrom": 3}
|
|
||||||
async with aiohttp.ClientSession() as session:
|
|
||||||
async with session.get(url, params=params, timeout=aiohttp.ClientTimeout(total=15)) as resp:
|
|
||||||
if resp.status != 200:
|
|
||||||
continue
|
|
||||||
scores_data = await resp.json()
|
|
||||||
except Exception:
|
|
||||||
continue
|
|
||||||
|
|
||||||
for event in scores_data:
|
|
||||||
if event.get("id") != match_id:
|
|
||||||
continue
|
|
||||||
if not event.get("completed"):
|
|
||||||
continue
|
|
||||||
|
|
||||||
scores = event.get("scores")
|
|
||||||
if not scores:
|
|
||||||
continue
|
|
||||||
|
|
||||||
winner = None
|
|
||||||
max_score = -1
|
|
||||||
for s in scores:
|
|
||||||
score_val = int(s.get("score", 0))
|
|
||||||
if score_val > max_score:
|
|
||||||
max_score = score_val
|
|
||||||
winner = s["name"]
|
|
||||||
|
|
||||||
if not winner:
|
|
||||||
continue
|
|
||||||
|
|
||||||
try:
|
|
||||||
with sqlite3.connect(config.BET_DB_PATH) as conn:
|
|
||||||
conn.row_factory = sqlite3.Row
|
|
||||||
bets = conn.execute(
|
|
||||||
"SELECT * FROM bets WHERE match_id = ? AND status = 'pending'",
|
|
||||||
(match_id,),
|
|
||||||
).fetchall()
|
|
||||||
|
|
||||||
for bet in bets:
|
|
||||||
if bet["chosen_team"] == winner:
|
|
||||||
winnings = round(bet["amount"] * bet["odds"], 1)
|
|
||||||
update_user_length(bet["user_id"], winnings)
|
|
||||||
conn.execute("UPDATE bets SET status = 'won' WHERE id = ?", (bet["id"],))
|
|
||||||
notifications.append(
|
|
||||||
f"🎉 user_id={bet['user_id']}: выиграл {winnings:.1f} см "
|
|
||||||
f"({bet['home_team']} vs {bet['away_team']}, {bet['chosen_team']})"
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
conn.execute("UPDATE bets SET status = 'lost' WHERE id = ?", (bet["id"],))
|
|
||||||
notifications.append(
|
|
||||||
f"❌ user_id={bet['user_id']}: проиграл {bet['amount']:.1f} см "
|
|
||||||
f"({bet['home_team']} vs {bet['away_team']}, {bet['chosen_team']})"
|
|
||||||
)
|
|
||||||
conn.commit()
|
|
||||||
except sqlite3.Error:
|
|
||||||
logger.exception("Failed to settle bets")
|
|
||||||
|
|
||||||
return notifications
|
|
||||||
73
main.py
73
main.py
|
|
@ -22,7 +22,6 @@ 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.casino import play_casino
|
||||||
from games.fortune import generate_fortune
|
from games.fortune import generate_fortune
|
||||||
from games.betting import get_formatted_matches, get_match_by_index, place_bet, get_user_bets, settle_bets
|
|
||||||
from zparser import get_military_data
|
from zparser import get_military_data
|
||||||
import config
|
import config
|
||||||
|
|
||||||
|
|
@ -1216,67 +1215,6 @@ async def handle_gadanie_cmd(message: Message):
|
||||||
logger.exception("Gadanie failed")
|
logger.exception("Gadanie failed")
|
||||||
await message.reply("Бля, карты рассыпались, попробуй позже.")
|
await message.reply("Бля, карты рассыпались, попробуй позже.")
|
||||||
|
|
||||||
async def handle_matches_cmd(message: Message):
|
|
||||||
parts = message.text.split(maxsplit=1)
|
|
||||||
sport_alias = parts[1].strip().lower() if len(parts) > 1 else None
|
|
||||||
await message.bot.send_chat_action(chat_id=message.chat.id, action="typing")
|
|
||||||
result = await get_formatted_matches(sport_alias)
|
|
||||||
await message.reply(result, parse_mode=None)
|
|
||||||
|
|
||||||
async def handle_bet_cmd(message: Message):
|
|
||||||
if not message.from_user:
|
|
||||||
await message.answer("Команда доступна только пользователям.")
|
|
||||||
return
|
|
||||||
parts = message.text.split()
|
|
||||||
if len(parts) < 4:
|
|
||||||
await message.reply(
|
|
||||||
"Формат: /bet <номер> <команда> <ставка>\n"
|
|
||||||
"Пример: /bet cs 1 NAVI 0.5\n"
|
|
||||||
"Спорт: cs / dota / football"
|
|
||||||
)
|
|
||||||
return
|
|
||||||
# /bet <спорт> <номер> <команда> <ставка>
|
|
||||||
if len(parts) >= 5:
|
|
||||||
sport_alias = parts[1].lower()
|
|
||||||
try:
|
|
||||||
index = int(parts[2])
|
|
||||||
except ValueError:
|
|
||||||
await message.reply("Номер матча должен быть числом.")
|
|
||||||
return
|
|
||||||
team = parts[3]
|
|
||||||
try:
|
|
||||||
amount = round(float(parts[4].replace(",", ".")), 1)
|
|
||||||
except ValueError:
|
|
||||||
await message.reply("Ставка должна быть числом.")
|
|
||||||
return
|
|
||||||
else:
|
|
||||||
await message.reply("Формат: /bet <спорт> <номер> <команда> <ставка>")
|
|
||||||
return
|
|
||||||
|
|
||||||
match = await get_match_by_index(sport_alias, index)
|
|
||||||
if not match:
|
|
||||||
await message.reply("Матч не найден. Посмотри /matches")
|
|
||||||
return
|
|
||||||
|
|
||||||
result = await asyncio.to_thread(place_bet, message.from_user.id, match, sport_alias, team, amount)
|
|
||||||
await message.reply(result, parse_mode=None)
|
|
||||||
|
|
||||||
async def handle_mybets_cmd(message: Message):
|
|
||||||
if not message.from_user:
|
|
||||||
return
|
|
||||||
result = await asyncio.to_thread(get_user_bets, message.from_user.id)
|
|
||||||
await message.reply(result, parse_mode=None)
|
|
||||||
|
|
||||||
async def _settle_loop(bot: Bot):
|
|
||||||
while True:
|
|
||||||
await asyncio.sleep(1800)
|
|
||||||
try:
|
|
||||||
notifications = await settle_bets()
|
|
||||||
for note in notifications:
|
|
||||||
logger.info(note)
|
|
||||||
except Exception:
|
|
||||||
logger.exception("settle_bets failed")
|
|
||||||
|
|
||||||
# --- Запуск ---
|
# --- Запуск ---
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
|
|
@ -1306,9 +1244,6 @@ def main():
|
||||||
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_penis_casino_cmd, Command("penis_casino"))
|
||||||
dp.message.register(handle_gadanie_cmd, Command("gadanie"))
|
dp.message.register(handle_gadanie_cmd, Command("gadanie"))
|
||||||
dp.message.register(handle_matches_cmd, Command("matches"))
|
|
||||||
dp.message.register(handle_bet_cmd, Command("bet"))
|
|
||||||
dp.message.register(handle_mybets_cmd, Command("mybets"))
|
|
||||||
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)
|
||||||
|
|
||||||
|
|
@ -1332,9 +1267,6 @@ def main():
|
||||||
BotCommand(command="ebalnik", description="вкл/выкл автответов"),
|
BotCommand(command="ebalnik", description="вкл/выкл автответов"),
|
||||||
BotCommand(command="penis_casino", description="казино на размер"),
|
BotCommand(command="penis_casino", description="казино на размер"),
|
||||||
BotCommand(command="gadanie", description="гадание на фене"),
|
BotCommand(command="gadanie", description="гадание на фене"),
|
||||||
BotCommand(command="matches", description="матчи для ставок"),
|
|
||||||
BotCommand(command="bet", description="поставить см на матч"),
|
|
||||||
BotCommand(command="mybets", description="мои ставки"),
|
|
||||||
BotCommand(command="svodka", description="СВО: итоги"),
|
BotCommand(command="svodka", description="СВО: итоги"),
|
||||||
]
|
]
|
||||||
scopes = (
|
scopes = (
|
||||||
|
|
@ -1348,11 +1280,6 @@ def main():
|
||||||
logger.warning("Не удалось выставить команды, но бот продолжит работу.")
|
logger.warning("Не удалось выставить команды, но бот продолжит работу.")
|
||||||
|
|
||||||
dp.startup.register(on_startup)
|
dp.startup.register(on_startup)
|
||||||
|
|
||||||
async def start_settle(bot: Bot):
|
|
||||||
asyncio.create_task(_settle_loop(bot))
|
|
||||||
dp.startup.register(start_settle)
|
|
||||||
|
|
||||||
logger.info("Бот запущен...")
|
logger.info("Бот запущен...")
|
||||||
asyncio.run(dp.start_polling(bot, allowed_updates=dp.resolve_used_update_types()))
|
asyncio.run(dp.start_polling(bot, allowed_updates=dp.resolve_used_update_types()))
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue