Welcome!

By registering with us, you'll be able to discuss, share and private message with other members of our community.

SignUp Now!

Что слить

Янв
243
531
Продавец
Всем привет, чет скучно. Скажите, что лучше сделать и слить
 
Решение
фреймворк вк апи

код:
import json
import random
import threading
import time
from typing import Dict, Any, List, Optional

from vk_api.keyboard import VkKeyboard, VkKeyboardColor


ROLE_MAFIA = "мафия"
ROLE_DETECTIVE = "комиссар"
ROLE_DOCTOR = "доктор"
ROLE_CIVIL = "мирный"

PHASE_LOBBY = "lobby"
PHASE_NIGHT = "night"
PHASE_DAY = "day"
PHASE_VOTE = "vote"
PHASE_ENDED = "ended"


class MafiaModule:
    def __init__(
        self,
        vk,
        *,
        admin_id: int = 0,
        night_sec: int = 60,
        day_sec: int = 90,
        vote_sec: int = 45,
        min_players: int = 3,
        max_players: int = 999,
    ):
        self.vk = vk
        self.admin_id = admin_id
        self.night_sec = night_sec
        self.day_sec...
Янв
243
531
Продавец
фреймворк вк апи

код:
import json
import random
import threading
import time
from typing import Dict, Any, List, Optional

from vk_api.keyboard import VkKeyboard, VkKeyboardColor


ROLE_MAFIA = "мафия"
ROLE_DETECTIVE = "комиссар"
ROLE_DOCTOR = "доктор"
ROLE_CIVIL = "мирный"

PHASE_LOBBY = "lobby"
PHASE_NIGHT = "night"
PHASE_DAY = "day"
PHASE_VOTE = "vote"
PHASE_ENDED = "ended"


class MafiaModule:
    def __init__(
        self,
        vk,
        *,
        admin_id: int = 0,
        night_sec: int = 60,
        day_sec: int = 90,
        vote_sec: int = 45,
        min_players: int = 3,
        max_players: int = 999,
    ):
        self.vk = vk
        self.admin_id = admin_id
        self.night_sec = night_sec
        self.day_sec = day_sec
        self.vote_sec = vote_sec
        self.min_players = min_players
        self.max_players = max_players

        self.games: Dict[int, Dict[str, Any]] = {}
        self.lock = threading.Lock()
        self.name_cache: Dict[int, str] = {}

        t = threading.Thread(target=self._ticker_loop, daemon=True)
        t.start()

    def _ticker_loop(self):
        while True:
            try:
                self._tick()
            except Exception:
                pass
            time.sleep(1)

    def _tick(self):
        with self.lock:
            games = list(self.games.values())
        now = time.time()
        for game in games:
            if not game.get("phase_end"):
                continue
            if now < game["phase_end"]:
                continue
            phase = game.get("phase")
            if phase == PHASE_NIGHT:
                self._resolve_night(game)
            elif phase == PHASE_DAY:
                self._begin_vote(game)
            elif phase == PHASE_VOTE:
                self._resolve_vote(game)

    def _get_name(self, user_id: int) -> str:
        if user_id in self.name_cache:
            return self.name_cache[user_id]
        try:
            info = self.vk.users.get(user_ids=user_id)[0]
            name = info["first_name"]
        except Exception:
            name = str(user_id)
        self.name_cache[user_id] = name
        return name

    def _mention(self, user_id: int) -> str:
        return f"[id{user_id}|{self._get_name(user_id)}]"

    def _is_chat(self, peer_id: int) -> bool:
        return peer_id >= 2000000000

    def _peer_to_chat_id(self, peer_id: int) -> int:
        return peer_id - 2000000000

    def _send(self, peer_id: int, text: str, keyboard: Optional[VkKeyboard] = None):
        params = {"peer_id": peer_id, "random_id": random.randint(1, 10**9), "message": text}
        if keyboard is not None:
            params["keyboard"] = keyboard.get_keyboard()
        self.vk.messages.send(**params)

    def _new_game(self, chat_id: int) -> Dict[str, Any]:
        return {
            "chat_id": chat_id,
            "phase": PHASE_LOBBY,
            "players": {},
            "phase_end": 0,
            "night": {},
            "day_votes": {},
            "round": 0,
        }

    def _get_game(self, chat_id: int) -> Dict[str, Any]:
        with self.lock:
            game = self.games.get(chat_id)
            if not game:
                game = self._new_game(chat_id)
                self.games[chat_id] = game
        return game

    def _player(self, game: Dict[str, Any], user_id: int):
        return game["players"].get(str(user_id))

    def _list_players(self, game: Dict[str, Any], alive_only=False) -> List[int]:
        ids = []
        for uid_str, p in game["players"].items():
            if alive_only and not p.get("alive"):
                continue
            ids.append(int(uid_str))
        return ids

    def _add_player(self, game: Dict[str, Any], user_id: int):
        if str(user_id) in game["players"]:
            return
        if len(game["players"]) >= self.max_players:
            return
        game["players"][str(user_id)] = {
            "name": self._get_name(user_id),
            "alive": True,
            "role": None,
        }

    def _remove_player(self, game: Dict[str, Any], user_id: int):
        if str(user_id) in game["players"]:
            del game["players"][str(user_id)]

    def _assign_roles(self, game: Dict[str, Any]):
        ids = self._list_players(game)
        random.shuffle(ids)
        mafia_count = max(1, len(ids) // 3)
        mafia_ids = ids[:mafia_count]
        rest = ids[mafia_count:]
        detective = rest[0] if len(rest) > 0 else None
        doctor = rest[1] if len(rest) > 1 else None

        for uid in ids:
            role = ROLE_CIVIL
            if uid in mafia_ids:
                role = ROLE_MAFIA
            elif detective == uid:
                role = ROLE_DETECTIVE
            elif doctor == uid:
                role = ROLE_DOCTOR
            game["players"][str(uid)]["role"] = role

    def _send_roles(self, game: Dict[str, Any]):
        mafia_ids = [uid for uid in self._list_players(game) if self._player(game, uid)["role"] == ROLE_MAFIA]
        mafia_names = ", ".join(self._mention(uid) for uid in mafia_ids)
        for uid in self._list_players(game):
            role = self._player(game, uid)["role"]
            if role == ROLE_MAFIA:
                text = f"🕶 Роль: МАФИЯ\nСоюзники: {mafia_names}\nНочью выбирай жертву."
            elif role == ROLE_DETECTIVE:
                text = "🕵️ Роль: КОМИССАР. Ночью проверяй игроков."
            elif role == ROLE_DOCTOR:
                text = "🩺 Роль: ДОКТОР. Ночью выбирай, кого спасти."
            else:
                text = "🙂 Роль: МИРНЫЙ. Днём ищи мафию."
            self._send(uid, text)

    def _build_lobby_keyboard(self):
        kb = VkKeyboard(one_time=False)
        kb.add_button("✅ Войти", VkKeyboardColor.POSITIVE, payload={"action": "join"})
        kb.add_button("🚪 Выйти", VkKeyboardColor.NEGATIVE, payload={"action": "leave"})
        kb.add_line()
        kb.add_button("🚀 Старт", VkKeyboardColor.PRIMARY, payload={"action": "start"})
        kb.add_button("📊 Статус", VkKeyboardColor.SECONDARY, payload={"action": "status"})
        kb.add_line()
        kb.add_button("🛑 Стоп", VkKeyboardColor.NEGATIVE, payload={"action": "stop"})
        return kb

    def _build_targets_keyboard(self, targets: List[int], action: str, chat_id: int):
        if len(targets) == 0 or len(targets) > 10:
            return None
        kb = VkKeyboard(one_time=True, inline=True)
        for uid in targets:
            kb.add_button(
                self._get_name(uid),
                VkKeyboardColor.PRIMARY,
                payload={"action": action, "chat_id": chat_id, "target": uid},
            )
        return kb

    def _start_game(self, game: Dict[str, Any], peer_id: int):
        if game["phase"] != PHASE_LOBBY:
            self._send(peer_id, "Игра уже идёт.")
            return
        if len(game["players"]) < self.min_players:
            self._send(peer_id, f"Нужно минимум {self.min_players} игроков.")
            return
        self._assign_roles(game)
        self._send_roles(game)
        game["round"] = 1
        self._begin_night(game)
        self._send(peer_id, "🎬 Игра началась! Ночь наступает...")

    def _begin_night(self, game: Dict[str, Any]):
        game["phase"] = PHASE_NIGHT
        game["phase_end"] = time.time() + self.night_sec
        game["night"] = {"mafia_votes": {}, "doctor": None, "detective": None}

        chat_peer = game["chat_id"] + 2000000000
        self._send(chat_peer, "🌙 Ночь. Все засыпают...")

        alive = self._list_players(game, alive_only=True)
        mafia_ids = [uid for uid in alive if self._player(game, uid)["role"] == ROLE_MAFIA]
        targetable = [uid for uid in alive if self._player(game, uid)["role"] != ROLE_MAFIA]

        for uid in mafia_ids:
            kb = self._build_targets_keyboard(targetable, "kill", game["chat_id"])
            if kb:
                self._send(uid, "Выберите жертву:", keyboard=kb)
            else:
                self._send(uid, "Выберите жертву: kill ID")

        for uid in alive:
            role = self._player(game, uid)["role"]
            if role == ROLE_DOCTOR:
                kb = self._build_targets_keyboard(alive, "heal", game["chat_id"])
                self._send(uid, "Кого лечить?" if kb else "Кого лечить? heal ID", keyboard=kb)
            if role == ROLE_DETECTIVE:
                kb = self._build_targets_keyboard(alive, "check", game["chat_id"])
                self._send(uid, "Кого проверить?" if kb else "Кого проверить? check ID", keyboard=kb)

    def _resolve_night(self, game: Dict[str, Any]):
        alive = self._list_players(game, alive_only=True)
        mafia_votes = game["night"].get("mafia_votes", {})
        doctor_target = game["night"].get("doctor")
        detective_target = game["night"].get("detective")

        kill_target = None
        if mafia_votes:
            max_votes = max(mafia_votes.values())
            top = [int(uid) for uid, v in mafia_votes.items() if v == max_votes]
            kill_target = random.choice(top)

        killed = None
        if kill_target and kill_target in alive and kill_target != doctor_target:
            self._player(game, kill_target)["alive"] = False
            killed = kill_target

        if detective_target and detective_target in alive:
            role = self._player(game, detective_target)["role"]
            msg = "🔎 Проверка: МАФИЯ" if role == ROLE_MAFIA else "🔎 Проверка: НЕ мафия"
            det_id = next((u for u in alive if self._player(game, u)["role"] == ROLE_DETECTIVE), None)
            if det_id:
                self._send(det_id, msg)

        chat_peer = game["chat_id"] + 2000000000
        if killed:
            self._send(chat_peer, f"☀️ Утро. Ночью был убит: {self._mention(killed)}")
        else:
            self._send(chat_peer, "☀️ Утро. Ночь прошла без жертв.")

        if self._check_win(game):
            return

        game["phase"] = PHASE_DAY
        game["phase_end"] = time.time() + self.day_sec
        game["day_votes"] = {}
        self._send(chat_peer, "🗣 День. Обсуждаем. Скоро голосование.")

    def _begin_vote(self, game: Dict[str, Any]):
        game["phase"] = PHASE_VOTE
        game["phase_end"] = time.time() + self.vote_sec
        game["day_votes"] = {}

        chat_peer = game["chat_id"] + 2000000000
        alive = self._list_players(game, alive_only=True)
        names = [self._mention(uid) for uid in alive]
        self._send(chat_peer, "🗳 Голосование! Кого линчуем?\n" + "\n".join(names))

        kb = self._build_targets_keyboard(alive, "vote", game["chat_id"])
        if kb:
            self._send(chat_peer, "Выберите голос кнопкой:", keyboard=kb)
        else:
            self._send(chat_peer, "Напишите: vote ID")

    def _resolve_vote(self, game: Dict[str, Any]):
        votes = game.get("day_votes", {})
        alive = self._list_players(game, alive_only=True)
        counted = {}
        for voter, target in votes.items():
            if int(voter) not in alive:
                continue
            if target not in alive:
                continue
            counted[target] = counted.get(target, 0) + 1

        chat_peer = game["chat_id"] + 2000000000
        if not counted:
            self._send(chat_peer, "Никого не линчуем.")
        else:
            max_votes = max(counted.values())
            top = [uid for uid, v in counted.items() if v == max_votes]
            lynched = random.choice(top)
            self._player(game, lynched)["alive"] = False
            self._send(chat_peer, f"⚖️ Линчевали: {self._mention(lynched)}")

        if self._check_win(game):
            return
        game["round"] += 1
        self._begin_night(game)

    def _check_win(self, game: Dict[str, Any]) -> bool:
        alive = self._list_players(game, alive_only=True)
        mafia = [uid for uid in alive if self._player(game, uid)["role"] == ROLE_MAFIA]
        civ = [uid for uid in alive if self._player(game, uid)["role"] != ROLE_MAFIA]
        chat_peer = game["chat_id"] + 2000000000

        if len(mafia) == 0:
            self._send(chat_peer, "🏆 Победа мирных!")
            game["phase"] = PHASE_ENDED
            game["phase_end"] = 0
            return True
        if len(mafia) >= len(civ):
            self._send(chat_peer, "😈 Победа мафии!")
            game["phase"] = PHASE_ENDED
            game["phase_end"] = 0
            return True
        return False

    def _status_text(self, game: Dict[str, Any]) -> str:
        players = []
        for uid in self._list_players(game):
            p = self._player(game, uid)
            status = "✅" if p.get("alive") else "💀"
            players.append(f"{status} {self._mention(uid)}")
        phase_map = {
            PHASE_LOBBY: "лобби",
            PHASE_NIGHT: "ночь",
            PHASE_DAY: "день",
            PHASE_VOTE: "голосование",
            PHASE_ENDED: "завершена",
        }
        return (
            f"🎮 Мафия\nФаза: {phase_map.get(game['phase'], game['phase'])}\n"
            f"Раунд: {game.get('round', 0)}\n\n"
            f"Игроки ({len(game['players'])}):\n" + "\n".join(players)
        )

    def _handle_payload(self, peer_id: int, user_id: int, payload: Dict[str, Any]):
        action = payload.get("action")
        chat_id = payload.get("chat_id")
        if action in {"join", "leave", "start", "status", "stop"}:
            if not self._is_chat(peer_id):
                return
            chat_id = self._peer_to_chat_id(peer_id)
            game = self._get_game(chat_id)
            if action == "join":
                self._add_player(game, user_id)
                self._send(peer_id, f"{self._mention(user_id)} в игре!")
            elif action == "leave":
                if game["phase"] == PHASE_LOBBY:
                    self._remove_player(game, user_id)
                    self._send(peer_id, f"{self._mention(user_id)} вышел.")
                else:
                    self._player(game, user_id)["alive"] = False
                    self._send(peer_id, f"{self._mention(user_id)} покинул игру и считается мёртвым.")
                    self._check_win(game)
            elif action == "start":
                self._start_game(game, peer_id)
            elif action == "status":
                self._send(peer_id, self._status_text(game), keyboard=self._build_lobby_keyboard())
            elif action == "stop":
                if self.admin_id and user_id != self.admin_id:
                    self._send(peer_id, "Только админ может стопнуть игру.")
                else:
                    game["phase"] = PHASE_ENDED
                    game["phase_end"] = 0
                    self._send(peer_id, "🛑 Игра остановлена.")
            return

        if action in {"kill", "heal", "check", "vote"}:
            if not chat_id:
                return
            game = self._get_game(int(chat_id))
            target = payload.get("target")
            if target is None:
                return
            self._handle_action(game, user_id, action, int(target))

    def _handle_action(self, game: Dict[str, Any], user_id: int, action: str, target: int):
        player = self._player(game, user_id)
        if not player or not player.get("alive"):
            return
        if game["phase"] == PHASE_NIGHT:
            if action == "kill" and player.get("role") == ROLE_MAFIA:
                game["night"]["mafia_votes"][str(target)] = game["night"]["mafia_votes"].get(str(target), 0) + 1
                self._send(user_id, "✅ Голос за убийство принят.")
            elif action == "heal" and player.get("role") == ROLE_DOCTOR:
                game["night"]["doctor"] = target
                self._send(user_id, "✅ Лечение принято.")
            elif action == "check" and player.get("role") == ROLE_DETECTIVE:
                game["night"]["detective"] = target
                self._send(user_id, "✅ Проверка принята.")
        elif game["phase"] == PHASE_VOTE and action == "vote":
            if not self._player(game, target) or not self._player(game, target).get("alive"):
                return
            game["day_votes"][str(user_id)] = target
            chat_peer = game["chat_id"] + 2000000000
            self._send(chat_peer, f"🗳 {self._mention(user_id)} голосует.")

    def _handle_text_action(self, peer_id: int, user_id: int, text: str):
        parts = text.strip().split()
        if len(parts) != 2:
            return
        cmd, target_str = parts[0].lower(), parts[1]
        if not target_str.isdigit():
            return
        target = int(target_str)
        if cmd not in {"kill", "heal", "check", "vote"}:
            return

        if self._is_chat(peer_id):
            chat_id = self._peer_to_chat_id(peer_id)
        else:
            chat_id = None
            for g in self.games.values():
                if str(user_id) in g.get("players", {}):
                    chat_id = g["chat_id"]
                    break
        if not chat_id:
            return
        game = self._get_game(chat_id)
        self._handle_action(game, user_id, cmd, target)

    def handle_message(self, event):
        msg = event.message
        peer_id = msg["peer_id"]
        user_id = msg["from_id"]
        text = (msg.get("text") or "").strip()

        payload_raw = msg.get("payload")
        if payload_raw:
            try:
                payload = json.loads(payload_raw)
                self._handle_payload(peer_id, user_id, payload)
                return
            except Exception:
                pass

        text_lower = text.lower()

        if self._is_chat(peer_id):
            chat_id = self._peer_to_chat_id(peer_id)
            game = self._get_game(chat_id)

            if text_lower in {"/mafia", "мафия", "игра"}:
                self._send(peer_id, self._status_text(game), keyboard=self._build_lobby_keyboard())
                return
            if text_lower in {"/help", "помощь"}:
                self._send(peer_id, "Команды: мафия, войти, выйти, старт, статус, стоп")
                return
            if text_lower in {"войти", "join"}:
                self._add_player(game, user_id)
                self._send(peer_id, f"{self._mention(user_id)} в игре!")
                return
            if text_lower in {"выйти", "leave"}:
                if game["phase"] == PHASE_LOBBY:
                    self._remove_player(game, user_id)
                    self._send(peer_id, f"{self._mention(user_id)} вышел.")
                else:
                    self._player(game, user_id)["alive"] = False
                    self._send(peer_id, f"{self._mention(user_id)} покинул игру и считается мёртвым.")
                    self._check_win(game)
                return
            if text_lower in {"старт", "start"}:
                self._start_game(game, peer_id)
                return
            if text_lower in {"статус", "status"}:
                self._send(peer_id, self._status_text(game), keyboard=self._build_lobby_keyboard())
                return
            if text_lower in {"стоп", "stop"}:
                if self.admin_id and user_id != self.admin_id:
                    self._send(peer_id, "Только админ может стопнуть игру.")
                else:
                    game["phase"] = PHASE_ENDED
                    game["phase_end"] = 0
                    self._send(peer_id, "🛑 Игра остановлена.")
                return

        self._handle_text_action(peer_id, user_id, text_lower)
 
Янв
70
51
Пользователь
фреймворк вк апи

код:
import json
import random
import threading
import time
from typing import Dict, Any, List, Optional

from vk_api.keyboard import VkKeyboard, VkKeyboardColor


ROLE_MAFIA = "мафия"
ROLE_DETECTIVE = "комиссар"
ROLE_DOCTOR = "доктор"
ROLE_CIVIL = "мирный"

PHASE_LOBBY = "lobby"
PHASE_NIGHT = "night"
PHASE_DAY = "day"
PHASE_VOTE = "vote"
PHASE_ENDED = "ended"


class MafiaModule:
    def __init__(
        self,
        vk,
        *,
        admin_id: int = 0,
        night_sec: int = 60,
        day_sec: int = 90,
        vote_sec: int = 45,
        min_players: int = 3,
        max_players: int = 999,
    ):
        self.vk = vk
        self.admin_id = admin_id
        self.night_sec = night_sec
        self.day_sec = day_sec
        self.vote_sec = vote_sec
        self.min_players = min_players
        self.max_players = max_players

        self.games: Dict[int, Dict[str, Any]] = {}
        self.lock = threading.Lock()
        self.name_cache: Dict[int, str] = {}

        t = threading.Thread(target=self._ticker_loop, daemon=True)
        t.start()

    def _ticker_loop(self):
        while True:
            try:
                self._tick()
            except Exception:
                pass
            time.sleep(1)

    def _tick(self):
        with self.lock:
            games = list(self.games.values())
        now = time.time()
        for game in games:
            if not game.get("phase_end"):
                continue
            if now < game["phase_end"]:
                continue
            phase = game.get("phase")
            if phase == PHASE_NIGHT:
                self._resolve_night(game)
            elif phase == PHASE_DAY:
                self._begin_vote(game)
            elif phase == PHASE_VOTE:
                self._resolve_vote(game)

    def _get_name(self, user_id: int) -> str:
        if user_id in self.name_cache:
            return self.name_cache[user_id]
        try:
            info = self.vk.users.get(user_ids=user_id)[0]
            name = info["first_name"]
        except Exception:
            name = str(user_id)
        self.name_cache[user_id] = name
        return name

    def _mention(self, user_id: int) -> str:
        return f"[id{user_id}|{self._get_name(user_id)}]"

    def _is_chat(self, peer_id: int) -> bool:
        return peer_id >= 2000000000

    def _peer_to_chat_id(self, peer_id: int) -> int:
        return peer_id - 2000000000

    def _send(self, peer_id: int, text: str, keyboard: Optional[VkKeyboard] = None):
        params = {"peer_id": peer_id, "random_id": random.randint(1, 10**9), "message": text}
        if keyboard is not None:
            params["keyboard"] = keyboard.get_keyboard()
        self.vk.messages.send(**params)

    def _new_game(self, chat_id: int) -> Dict[str, Any]:
        return {
            "chat_id": chat_id,
            "phase": PHASE_LOBBY,
            "players": {},
            "phase_end": 0,
            "night": {},
            "day_votes": {},
            "round": 0,
        }

    def _get_game(self, chat_id: int) -> Dict[str, Any]:
        with self.lock:
            game = self.games.get(chat_id)
            if not game:
                game = self._new_game(chat_id)
                self.games[chat_id] = game
        return game

    def _player(self, game: Dict[str, Any], user_id: int):
        return game["players"].get(str(user_id))

    def _list_players(self, game: Dict[str, Any], alive_only=False) -> List[int]:
        ids = []
        for uid_str, p in game["players"].items():
            if alive_only and not p.get("alive"):
                continue
            ids.append(int(uid_str))
        return ids

    def _add_player(self, game: Dict[str, Any], user_id: int):
        if str(user_id) in game["players"]:
            return
        if len(game["players"]) >= self.max_players:
            return
        game["players"][str(user_id)] = {
            "name": self._get_name(user_id),
            "alive": True,
            "role": None,
        }

    def _remove_player(self, game: Dict[str, Any], user_id: int):
        if str(user_id) in game["players"]:
            del game["players"][str(user_id)]

    def _assign_roles(self, game: Dict[str, Any]):
        ids = self._list_players(game)
        random.shuffle(ids)
        mafia_count = max(1, len(ids) // 3)
        mafia_ids = ids[:mafia_count]
        rest = ids[mafia_count:]
        detective = rest[0] if len(rest) > 0 else None
        doctor = rest[1] if len(rest) > 1 else None

        for uid in ids:
            role = ROLE_CIVIL
            if uid in mafia_ids:
                role = ROLE_MAFIA
            elif detective == uid:
                role = ROLE_DETECTIVE
            elif doctor == uid:
                role = ROLE_DOCTOR
            game["players"][str(uid)]["role"] = role

    def _send_roles(self, game: Dict[str, Any]):
        mafia_ids = [uid for uid in self._list_players(game) if self._player(game, uid)["role"] == ROLE_MAFIA]
        mafia_names = ", ".join(self._mention(uid) for uid in mafia_ids)
        for uid in self._list_players(game):
            role = self._player(game, uid)["role"]
            if role == ROLE_MAFIA:
                text = f"🕶 Роль: МАФИЯ\nСоюзники: {mafia_names}\nНочью выбирай жертву."
            elif role == ROLE_DETECTIVE:
                text = "🕵️ Роль: КОМИССАР. Ночью проверяй игроков."
            elif role == ROLE_DOCTOR:
                text = "🩺 Роль: ДОКТОР. Ночью выбирай, кого спасти."
            else:
                text = "🙂 Роль: МИРНЫЙ. Днём ищи мафию."
            self._send(uid, text)

    def _build_lobby_keyboard(self):
        kb = VkKeyboard(one_time=False)
        kb.add_button("✅ Войти", VkKeyboardColor.POSITIVE, payload={"action": "join"})
        kb.add_button("🚪 Выйти", VkKeyboardColor.NEGATIVE, payload={"action": "leave"})
        kb.add_line()
        kb.add_button("🚀 Старт", VkKeyboardColor.PRIMARY, payload={"action": "start"})
        kb.add_button("📊 Статус", VkKeyboardColor.SECONDARY, payload={"action": "status"})
        kb.add_line()
        kb.add_button("🛑 Стоп", VkKeyboardColor.NEGATIVE, payload={"action": "stop"})
        return kb

    def _build_targets_keyboard(self, targets: List[int], action: str, chat_id: int):
        if len(targets) == 0 or len(targets) > 10:
            return None
        kb = VkKeyboard(one_time=True, inline=True)
        for uid in targets:
            kb.add_button(
                self._get_name(uid),
                VkKeyboardColor.PRIMARY,
                payload={"action": action, "chat_id": chat_id, "target": uid},
            )
        return kb

    def _start_game(self, game: Dict[str, Any], peer_id: int):
        if game["phase"] != PHASE_LOBBY:
            self._send(peer_id, "Игра уже идёт.")
            return
        if len(game["players"]) < self.min_players:
            self._send(peer_id, f"Нужно минимум {self.min_players} игроков.")
            return
        self._assign_roles(game)
        self._send_roles(game)
        game["round"] = 1
        self._begin_night(game)
        self._send(peer_id, "🎬 Игра началась! Ночь наступает...")

    def _begin_night(self, game: Dict[str, Any]):
        game["phase"] = PHASE_NIGHT
        game["phase_end"] = time.time() + self.night_sec
        game["night"] = {"mafia_votes": {}, "doctor": None, "detective": None}

        chat_peer = game["chat_id"] + 2000000000
        self._send(chat_peer, "🌙 Ночь. Все засыпают...")

        alive = self._list_players(game, alive_only=True)
        mafia_ids = [uid for uid in alive if self._player(game, uid)["role"] == ROLE_MAFIA]
        targetable = [uid for uid in alive if self._player(game, uid)["role"] != ROLE_MAFIA]

        for uid in mafia_ids:
            kb = self._build_targets_keyboard(targetable, "kill", game["chat_id"])
            if kb:
                self._send(uid, "Выберите жертву:", keyboard=kb)
            else:
                self._send(uid, "Выберите жертву: kill ID")

        for uid in alive:
            role = self._player(game, uid)["role"]
            if role == ROLE_DOCTOR:
                kb = self._build_targets_keyboard(alive, "heal", game["chat_id"])
                self._send(uid, "Кого лечить?" if kb else "Кого лечить? heal ID", keyboard=kb)
            if role == ROLE_DETECTIVE:
                kb = self._build_targets_keyboard(alive, "check", game["chat_id"])
                self._send(uid, "Кого проверить?" if kb else "Кого проверить? check ID", keyboard=kb)

    def _resolve_night(self, game: Dict[str, Any]):
        alive = self._list_players(game, alive_only=True)
        mafia_votes = game["night"].get("mafia_votes", {})
        doctor_target = game["night"].get("doctor")
        detective_target = game["night"].get("detective")

        kill_target = None
        if mafia_votes:
            max_votes = max(mafia_votes.values())
            top = [int(uid) for uid, v in mafia_votes.items() if v == max_votes]
            kill_target = random.choice(top)

        killed = None
        if kill_target and kill_target in alive and kill_target != doctor_target:
            self._player(game, kill_target)["alive"] = False
            killed = kill_target

        if detective_target and detective_target in alive:
            role = self._player(game, detective_target)["role"]
            msg = "🔎 Проверка: МАФИЯ" if role == ROLE_MAFIA else "🔎 Проверка: НЕ мафия"
            det_id = next((u for u in alive if self._player(game, u)["role"] == ROLE_DETECTIVE), None)
            if det_id:
                self._send(det_id, msg)

        chat_peer = game["chat_id"] + 2000000000
        if killed:
            self._send(chat_peer, f"☀️ Утро. Ночью был убит: {self._mention(killed)}")
        else:
            self._send(chat_peer, "☀️ Утро. Ночь прошла без жертв.")

        if self._check_win(game):
            return

        game["phase"] = PHASE_DAY
        game["phase_end"] = time.time() + self.day_sec
        game["day_votes"] = {}
        self._send(chat_peer, "🗣 День. Обсуждаем. Скоро голосование.")

    def _begin_vote(self, game: Dict[str, Any]):
        game["phase"] = PHASE_VOTE
        game["phase_end"] = time.time() + self.vote_sec
        game["day_votes"] = {}

        chat_peer = game["chat_id"] + 2000000000
        alive = self._list_players(game, alive_only=True)
        names = [self._mention(uid) for uid in alive]
        self._send(chat_peer, "🗳 Голосование! Кого линчуем?\n" + "\n".join(names))

        kb = self._build_targets_keyboard(alive, "vote", game["chat_id"])
        if kb:
            self._send(chat_peer, "Выберите голос кнопкой:", keyboard=kb)
        else:
            self._send(chat_peer, "Напишите: vote ID")

    def _resolve_vote(self, game: Dict[str, Any]):
        votes = game.get("day_votes", {})
        alive = self._list_players(game, alive_only=True)
        counted = {}
        for voter, target in votes.items():
            if int(voter) not in alive:
                continue
            if target not in alive:
                continue
            counted[target] = counted.get(target, 0) + 1

        chat_peer = game["chat_id"] + 2000000000
        if not counted:
            self._send(chat_peer, "Никого не линчуем.")
        else:
            max_votes = max(counted.values())
            top = [uid for uid, v in counted.items() if v == max_votes]
            lynched = random.choice(top)
            self._player(game, lynched)["alive"] = False
            self._send(chat_peer, f"⚖️ Линчевали: {self._mention(lynched)}")

        if self._check_win(game):
            return
        game["round"] += 1
        self._begin_night(game)

    def _check_win(self, game: Dict[str, Any]) -> bool:
        alive = self._list_players(game, alive_only=True)
        mafia = [uid for uid in alive if self._player(game, uid)["role"] == ROLE_MAFIA]
        civ = [uid for uid in alive if self._player(game, uid)["role"] != ROLE_MAFIA]
        chat_peer = game["chat_id"] + 2000000000

        if len(mafia) == 0:
            self._send(chat_peer, "🏆 Победа мирных!")
            game["phase"] = PHASE_ENDED
            game["phase_end"] = 0
            return True
        if len(mafia) >= len(civ):
            self._send(chat_peer, "😈 Победа мафии!")
            game["phase"] = PHASE_ENDED
            game["phase_end"] = 0
            return True
        return False

    def _status_text(self, game: Dict[str, Any]) -> str:
        players = []
        for uid in self._list_players(game):
            p = self._player(game, uid)
            status = "✅" if p.get("alive") else "💀"
            players.append(f"{status} {self._mention(uid)}")
        phase_map = {
            PHASE_LOBBY: "лобби",
            PHASE_NIGHT: "ночь",
            PHASE_DAY: "день",
            PHASE_VOTE: "голосование",
            PHASE_ENDED: "завершена",
        }
        return (
            f"🎮 Мафия\nФаза: {phase_map.get(game['phase'], game['phase'])}\n"
            f"Раунд: {game.get('round', 0)}\n\n"
            f"Игроки ({len(game['players'])}):\n" + "\n".join(players)
        )

    def _handle_payload(self, peer_id: int, user_id: int, payload: Dict[str, Any]):
        action = payload.get("action")
        chat_id = payload.get("chat_id")
        if action in {"join", "leave", "start", "status", "stop"}:
            if not self._is_chat(peer_id):
                return
            chat_id = self._peer_to_chat_id(peer_id)
            game = self._get_game(chat_id)
            if action == "join":
                self._add_player(game, user_id)
                self._send(peer_id, f"{self._mention(user_id)} в игре!")
            elif action == "leave":
                if game["phase"] == PHASE_LOBBY:
                    self._remove_player(game, user_id)
                    self._send(peer_id, f"{self._mention(user_id)} вышел.")
                else:
                    self._player(game, user_id)["alive"] = False
                    self._send(peer_id, f"{self._mention(user_id)} покинул игру и считается мёртвым.")
                    self._check_win(game)
            elif action == "start":
                self._start_game(game, peer_id)
            elif action == "status":
                self._send(peer_id, self._status_text(game), keyboard=self._build_lobby_keyboard())
            elif action == "stop":
                if self.admin_id and user_id != self.admin_id:
                    self._send(peer_id, "Только админ может стопнуть игру.")
                else:
                    game["phase"] = PHASE_ENDED
                    game["phase_end"] = 0
                    self._send(peer_id, "🛑 Игра остановлена.")
            return

        if action in {"kill", "heal", "check", "vote"}:
            if not chat_id:
                return
            game = self._get_game(int(chat_id))
            target = payload.get("target")
            if target is None:
                return
            self._handle_action(game, user_id, action, int(target))

    def _handle_action(self, game: Dict[str, Any], user_id: int, action: str, target: int):
        player = self._player(game, user_id)
        if not player or not player.get("alive"):
            return
        if game["phase"] == PHASE_NIGHT:
            if action == "kill" and player.get("role") == ROLE_MAFIA:
                game["night"]["mafia_votes"][str(target)] = game["night"]["mafia_votes"].get(str(target), 0) + 1
                self._send(user_id, "✅ Голос за убийство принят.")
            elif action == "heal" and player.get("role") == ROLE_DOCTOR:
                game["night"]["doctor"] = target
                self._send(user_id, "✅ Лечение принято.")
            elif action == "check" and player.get("role") == ROLE_DETECTIVE:
                game["night"]["detective"] = target
                self._send(user_id, "✅ Проверка принята.")
        elif game["phase"] == PHASE_VOTE and action == "vote":
            if not self._player(game, target) or not self._player(game, target).get("alive"):
                return
            game["day_votes"][str(user_id)] = target
            chat_peer = game["chat_id"] + 2000000000
            self._send(chat_peer, f"🗳 {self._mention(user_id)} голосует.")

    def _handle_text_action(self, peer_id: int, user_id: int, text: str):
        parts = text.strip().split()
        if len(parts) != 2:
            return
        cmd, target_str = parts[0].lower(), parts[1]
        if not target_str.isdigit():
            return
        target = int(target_str)
        if cmd not in {"kill", "heal", "check", "vote"}:
            return

        if self._is_chat(peer_id):
            chat_id = self._peer_to_chat_id(peer_id)
        else:
            chat_id = None
            for g in self.games.values():
                if str(user_id) in g.get("players", {}):
                    chat_id = g["chat_id"]
                    break
        if not chat_id:
            return
        game = self._get_game(chat_id)
        self._handle_action(game, user_id, cmd, target)

    def handle_message(self, event):
        msg = event.message
        peer_id = msg["peer_id"]
        user_id = msg["from_id"]
        text = (msg.get("text") or "").strip()

        payload_raw = msg.get("payload")
        if payload_raw:
            try:
                payload = json.loads(payload_raw)
                self._handle_payload(peer_id, user_id, payload)
                return
            except Exception:
                pass

        text_lower = text.lower()

        if self._is_chat(peer_id):
            chat_id = self._peer_to_chat_id(peer_id)
            game = self._get_game(chat_id)

            if text_lower in {"/mafia", "мафия", "игра"}:
                self._send(peer_id, self._status_text(game), keyboard=self._build_lobby_keyboard())
                return
            if text_lower in {"/help", "помощь"}:
                self._send(peer_id, "Команды: мафия, войти, выйти, старт, статус, стоп")
                return
            if text_lower in {"войти", "join"}:
                self._add_player(game, user_id)
                self._send(peer_id, f"{self._mention(user_id)} в игре!")
                return
            if text_lower in {"выйти", "leave"}:
                if game["phase"] == PHASE_LOBBY:
                    self._remove_player(game, user_id)
                    self._send(peer_id, f"{self._mention(user_id)} вышел.")
                else:
                    self._player(game, user_id)["alive"] = False
                    self._send(peer_id, f"{self._mention(user_id)} покинул игру и считается мёртвым.")
                    self._check_win(game)
                return
            if text_lower in {"старт", "start"}:
                self._start_game(game, peer_id)
                return
            if text_lower in {"статус", "status"}:
                self._send(peer_id, self._status_text(game), keyboard=self._build_lobby_keyboard())
                return
            if text_lower in {"стоп", "stop"}:
                if self.admin_id and user_id != self.admin_id:
                    self._send(peer_id, "Только админ может стопнуть игру.")
                else:
                    game["phase"] = PHASE_ENDED
                    game["phase_end"] = 0
                    self._send(peer_id, "🛑 Игра остановлена.")
                return

        self._handle_text_action(peer_id, user_id, text_lower)
js bot work будет?
 
Янв
70
51
Пользователь
фреймворк вк апи

код:
import json
import random
import threading
import time
from typing import Dict, Any, List, Optional

from vk_api.keyboard import VkKeyboard, VkKeyboardColor


ROLE_MAFIA = "мафия"
ROLE_DETECTIVE = "комиссар"
ROLE_DOCTOR = "доктор"
ROLE_CIVIL = "мирный"

PHASE_LOBBY = "lobby"
PHASE_NIGHT = "night"
PHASE_DAY = "day"
PHASE_VOTE = "vote"
PHASE_ENDED = "ended"


class MafiaModule:
    def __init__(
        self,
        vk,
        *,
        admin_id: int = 0,
        night_sec: int = 60,
        day_sec: int = 90,
        vote_sec: int = 45,
        min_players: int = 3,
        max_players: int = 999,
    ):
        self.vk = vk
        self.admin_id = admin_id
        self.night_sec = night_sec
        self.day_sec = day_sec
        self.vote_sec = vote_sec
        self.min_players = min_players
        self.max_players = max_players

        self.games: Dict[int, Dict[str, Any]] = {}
        self.lock = threading.Lock()
        self.name_cache: Dict[int, str] = {}

        t = threading.Thread(target=self._ticker_loop, daemon=True)
        t.start()

    def _ticker_loop(self):
        while True:
            try:
                self._tick()
            except Exception:
                pass
            time.sleep(1)

    def _tick(self):
        with self.lock:
            games = list(self.games.values())
        now = time.time()
        for game in games:
            if not game.get("phase_end"):
                continue
            if now < game["phase_end"]:
                continue
            phase = game.get("phase")
            if phase == PHASE_NIGHT:
                self._resolve_night(game)
            elif phase == PHASE_DAY:
                self._begin_vote(game)
            elif phase == PHASE_VOTE:
                self._resolve_vote(game)

    def _get_name(self, user_id: int) -> str:
        if user_id in self.name_cache:
            return self.name_cache[user_id]
        try:
            info = self.vk.users.get(user_ids=user_id)[0]
            name = info["first_name"]
        except Exception:
            name = str(user_id)
        self.name_cache[user_id] = name
        return name

    def _mention(self, user_id: int) -> str:
        return f"[id{user_id}|{self._get_name(user_id)}]"

    def _is_chat(self, peer_id: int) -> bool:
        return peer_id >= 2000000000

    def _peer_to_chat_id(self, peer_id: int) -> int:
        return peer_id - 2000000000

    def _send(self, peer_id: int, text: str, keyboard: Optional[VkKeyboard] = None):
        params = {"peer_id": peer_id, "random_id": random.randint(1, 10**9), "message": text}
        if keyboard is not None:
            params["keyboard"] = keyboard.get_keyboard()
        self.vk.messages.send(**params)

    def _new_game(self, chat_id: int) -> Dict[str, Any]:
        return {
            "chat_id": chat_id,
            "phase": PHASE_LOBBY,
            "players": {},
            "phase_end": 0,
            "night": {},
            "day_votes": {},
            "round": 0,
        }

    def _get_game(self, chat_id: int) -> Dict[str, Any]:
        with self.lock:
            game = self.games.get(chat_id)
            if not game:
                game = self._new_game(chat_id)
                self.games[chat_id] = game
        return game

    def _player(self, game: Dict[str, Any], user_id: int):
        return game["players"].get(str(user_id))

    def _list_players(self, game: Dict[str, Any], alive_only=False) -> List[int]:
        ids = []
        for uid_str, p in game["players"].items():
            if alive_only and not p.get("alive"):
                continue
            ids.append(int(uid_str))
        return ids

    def _add_player(self, game: Dict[str, Any], user_id: int):
        if str(user_id) in game["players"]:
            return
        if len(game["players"]) >= self.max_players:
            return
        game["players"][str(user_id)] = {
            "name": self._get_name(user_id),
            "alive": True,
            "role": None,
        }

    def _remove_player(self, game: Dict[str, Any], user_id: int):
        if str(user_id) in game["players"]:
            del game["players"][str(user_id)]

    def _assign_roles(self, game: Dict[str, Any]):
        ids = self._list_players(game)
        random.shuffle(ids)
        mafia_count = max(1, len(ids) // 3)
        mafia_ids = ids[:mafia_count]
        rest = ids[mafia_count:]
        detective = rest[0] if len(rest) > 0 else None
        doctor = rest[1] if len(rest) > 1 else None

        for uid in ids:
            role = ROLE_CIVIL
            if uid in mafia_ids:
                role = ROLE_MAFIA
            elif detective == uid:
                role = ROLE_DETECTIVE
            elif doctor == uid:
                role = ROLE_DOCTOR
            game["players"][str(uid)]["role"] = role

    def _send_roles(self, game: Dict[str, Any]):
        mafia_ids = [uid for uid in self._list_players(game) if self._player(game, uid)["role"] == ROLE_MAFIA]
        mafia_names = ", ".join(self._mention(uid) for uid in mafia_ids)
        for uid in self._list_players(game):
            role = self._player(game, uid)["role"]
            if role == ROLE_MAFIA:
                text = f"🕶 Роль: МАФИЯ\nСоюзники: {mafia_names}\nНочью выбирай жертву."
            elif role == ROLE_DETECTIVE:
                text = "🕵️ Роль: КОМИССАР. Ночью проверяй игроков."
            elif role == ROLE_DOCTOR:
                text = "🩺 Роль: ДОКТОР. Ночью выбирай, кого спасти."
            else:
                text = "🙂 Роль: МИРНЫЙ. Днём ищи мафию."
            self._send(uid, text)

    def _build_lobby_keyboard(self):
        kb = VkKeyboard(one_time=False)
        kb.add_button("✅ Войти", VkKeyboardColor.POSITIVE, payload={"action": "join"})
        kb.add_button("🚪 Выйти", VkKeyboardColor.NEGATIVE, payload={"action": "leave"})
        kb.add_line()
        kb.add_button("🚀 Старт", VkKeyboardColor.PRIMARY, payload={"action": "start"})
        kb.add_button("📊 Статус", VkKeyboardColor.SECONDARY, payload={"action": "status"})
        kb.add_line()
        kb.add_button("🛑 Стоп", VkKeyboardColor.NEGATIVE, payload={"action": "stop"})
        return kb

    def _build_targets_keyboard(self, targets: List[int], action: str, chat_id: int):
        if len(targets) == 0 or len(targets) > 10:
            return None
        kb = VkKeyboard(one_time=True, inline=True)
        for uid in targets:
            kb.add_button(
                self._get_name(uid),
                VkKeyboardColor.PRIMARY,
                payload={"action": action, "chat_id": chat_id, "target": uid},
            )
        return kb

    def _start_game(self, game: Dict[str, Any], peer_id: int):
        if game["phase"] != PHASE_LOBBY:
            self._send(peer_id, "Игра уже идёт.")
            return
        if len(game["players"]) < self.min_players:
            self._send(peer_id, f"Нужно минимум {self.min_players} игроков.")
            return
        self._assign_roles(game)
        self._send_roles(game)
        game["round"] = 1
        self._begin_night(game)
        self._send(peer_id, "🎬 Игра началась! Ночь наступает...")

    def _begin_night(self, game: Dict[str, Any]):
        game["phase"] = PHASE_NIGHT
        game["phase_end"] = time.time() + self.night_sec
        game["night"] = {"mafia_votes": {}, "doctor": None, "detective": None}

        chat_peer = game["chat_id"] + 2000000000
        self._send(chat_peer, "🌙 Ночь. Все засыпают...")

        alive = self._list_players(game, alive_only=True)
        mafia_ids = [uid for uid in alive if self._player(game, uid)["role"] == ROLE_MAFIA]
        targetable = [uid for uid in alive if self._player(game, uid)["role"] != ROLE_MAFIA]

        for uid in mafia_ids:
            kb = self._build_targets_keyboard(targetable, "kill", game["chat_id"])
            if kb:
                self._send(uid, "Выберите жертву:", keyboard=kb)
            else:
                self._send(uid, "Выберите жертву: kill ID")

        for uid in alive:
            role = self._player(game, uid)["role"]
            if role == ROLE_DOCTOR:
                kb = self._build_targets_keyboard(alive, "heal", game["chat_id"])
                self._send(uid, "Кого лечить?" if kb else "Кого лечить? heal ID", keyboard=kb)
            if role == ROLE_DETECTIVE:
                kb = self._build_targets_keyboard(alive, "check", game["chat_id"])
                self._send(uid, "Кого проверить?" if kb else "Кого проверить? check ID", keyboard=kb)

    def _resolve_night(self, game: Dict[str, Any]):
        alive = self._list_players(game, alive_only=True)
        mafia_votes = game["night"].get("mafia_votes", {})
        doctor_target = game["night"].get("doctor")
        detective_target = game["night"].get("detective")

        kill_target = None
        if mafia_votes:
            max_votes = max(mafia_votes.values())
            top = [int(uid) for uid, v in mafia_votes.items() if v == max_votes]
            kill_target = random.choice(top)

        killed = None
        if kill_target and kill_target in alive and kill_target != doctor_target:
            self._player(game, kill_target)["alive"] = False
            killed = kill_target

        if detective_target and detective_target in alive:
            role = self._player(game, detective_target)["role"]
            msg = "🔎 Проверка: МАФИЯ" if role == ROLE_MAFIA else "🔎 Проверка: НЕ мафия"
            det_id = next((u for u in alive if self._player(game, u)["role"] == ROLE_DETECTIVE), None)
            if det_id:
                self._send(det_id, msg)

        chat_peer = game["chat_id"] + 2000000000
        if killed:
            self._send(chat_peer, f"☀️ Утро. Ночью был убит: {self._mention(killed)}")
        else:
            self._send(chat_peer, "☀️ Утро. Ночь прошла без жертв.")

        if self._check_win(game):
            return

        game["phase"] = PHASE_DAY
        game["phase_end"] = time.time() + self.day_sec
        game["day_votes"] = {}
        self._send(chat_peer, "🗣 День. Обсуждаем. Скоро голосование.")

    def _begin_vote(self, game: Dict[str, Any]):
        game["phase"] = PHASE_VOTE
        game["phase_end"] = time.time() + self.vote_sec
        game["day_votes"] = {}

        chat_peer = game["chat_id"] + 2000000000
        alive = self._list_players(game, alive_only=True)
        names = [self._mention(uid) for uid in alive]
        self._send(chat_peer, "🗳 Голосование! Кого линчуем?\n" + "\n".join(names))

        kb = self._build_targets_keyboard(alive, "vote", game["chat_id"])
        if kb:
            self._send(chat_peer, "Выберите голос кнопкой:", keyboard=kb)
        else:
            self._send(chat_peer, "Напишите: vote ID")

    def _resolve_vote(self, game: Dict[str, Any]):
        votes = game.get("day_votes", {})
        alive = self._list_players(game, alive_only=True)
        counted = {}
        for voter, target in votes.items():
            if int(voter) not in alive:
                continue
            if target not in alive:
                continue
            counted[target] = counted.get(target, 0) + 1

        chat_peer = game["chat_id"] + 2000000000
        if not counted:
            self._send(chat_peer, "Никого не линчуем.")
        else:
            max_votes = max(counted.values())
            top = [uid for uid, v in counted.items() if v == max_votes]
            lynched = random.choice(top)
            self._player(game, lynched)["alive"] = False
            self._send(chat_peer, f"⚖️ Линчевали: {self._mention(lynched)}")

        if self._check_win(game):
            return
        game["round"] += 1
        self._begin_night(game)

    def _check_win(self, game: Dict[str, Any]) -> bool:
        alive = self._list_players(game, alive_only=True)
        mafia = [uid for uid in alive if self._player(game, uid)["role"] == ROLE_MAFIA]
        civ = [uid for uid in alive if self._player(game, uid)["role"] != ROLE_MAFIA]
        chat_peer = game["chat_id"] + 2000000000

        if len(mafia) == 0:
            self._send(chat_peer, "🏆 Победа мирных!")
            game["phase"] = PHASE_ENDED
            game["phase_end"] = 0
            return True
        if len(mafia) >= len(civ):
            self._send(chat_peer, "😈 Победа мафии!")
            game["phase"] = PHASE_ENDED
            game["phase_end"] = 0
            return True
        return False

    def _status_text(self, game: Dict[str, Any]) -> str:
        players = []
        for uid in self._list_players(game):
            p = self._player(game, uid)
            status = "✅" if p.get("alive") else "💀"
            players.append(f"{status} {self._mention(uid)}")
        phase_map = {
            PHASE_LOBBY: "лобби",
            PHASE_NIGHT: "ночь",
            PHASE_DAY: "день",
            PHASE_VOTE: "голосование",
            PHASE_ENDED: "завершена",
        }
        return (
            f"🎮 Мафия\nФаза: {phase_map.get(game['phase'], game['phase'])}\n"
            f"Раунд: {game.get('round', 0)}\n\n"
            f"Игроки ({len(game['players'])}):\n" + "\n".join(players)
        )

    def _handle_payload(self, peer_id: int, user_id: int, payload: Dict[str, Any]):
        action = payload.get("action")
        chat_id = payload.get("chat_id")
        if action in {"join", "leave", "start", "status", "stop"}:
            if not self._is_chat(peer_id):
                return
            chat_id = self._peer_to_chat_id(peer_id)
            game = self._get_game(chat_id)
            if action == "join":
                self._add_player(game, user_id)
                self._send(peer_id, f"{self._mention(user_id)} в игре!")
            elif action == "leave":
                if game["phase"] == PHASE_LOBBY:
                    self._remove_player(game, user_id)
                    self._send(peer_id, f"{self._mention(user_id)} вышел.")
                else:
                    self._player(game, user_id)["alive"] = False
                    self._send(peer_id, f"{self._mention(user_id)} покинул игру и считается мёртвым.")
                    self._check_win(game)
            elif action == "start":
                self._start_game(game, peer_id)
            elif action == "status":
                self._send(peer_id, self._status_text(game), keyboard=self._build_lobby_keyboard())
            elif action == "stop":
                if self.admin_id and user_id != self.admin_id:
                    self._send(peer_id, "Только админ может стопнуть игру.")
                else:
                    game["phase"] = PHASE_ENDED
                    game["phase_end"] = 0
                    self._send(peer_id, "🛑 Игра остановлена.")
            return

        if action in {"kill", "heal", "check", "vote"}:
            if not chat_id:
                return
            game = self._get_game(int(chat_id))
            target = payload.get("target")
            if target is None:
                return
            self._handle_action(game, user_id, action, int(target))

    def _handle_action(self, game: Dict[str, Any], user_id: int, action: str, target: int):
        player = self._player(game, user_id)
        if not player or not player.get("alive"):
            return
        if game["phase"] == PHASE_NIGHT:
            if action == "kill" and player.get("role") == ROLE_MAFIA:
                game["night"]["mafia_votes"][str(target)] = game["night"]["mafia_votes"].get(str(target), 0) + 1
                self._send(user_id, "✅ Голос за убийство принят.")
            elif action == "heal" and player.get("role") == ROLE_DOCTOR:
                game["night"]["doctor"] = target
                self._send(user_id, "✅ Лечение принято.")
            elif action == "check" and player.get("role") == ROLE_DETECTIVE:
                game["night"]["detective"] = target
                self._send(user_id, "✅ Проверка принята.")
        elif game["phase"] == PHASE_VOTE and action == "vote":
            if not self._player(game, target) or not self._player(game, target).get("alive"):
                return
            game["day_votes"][str(user_id)] = target
            chat_peer = game["chat_id"] + 2000000000
            self._send(chat_peer, f"🗳 {self._mention(user_id)} голосует.")

    def _handle_text_action(self, peer_id: int, user_id: int, text: str):
        parts = text.strip().split()
        if len(parts) != 2:
            return
        cmd, target_str = parts[0].lower(), parts[1]
        if not target_str.isdigit():
            return
        target = int(target_str)
        if cmd not in {"kill", "heal", "check", "vote"}:
            return

        if self._is_chat(peer_id):
            chat_id = self._peer_to_chat_id(peer_id)
        else:
            chat_id = None
            for g in self.games.values():
                if str(user_id) in g.get("players", {}):
                    chat_id = g["chat_id"]
                    break
        if not chat_id:
            return
        game = self._get_game(chat_id)
        self._handle_action(game, user_id, cmd, target)

    def handle_message(self, event):
        msg = event.message
        peer_id = msg["peer_id"]
        user_id = msg["from_id"]
        text = (msg.get("text") or "").strip()

        payload_raw = msg.get("payload")
        if payload_raw:
            try:
                payload = json.loads(payload_raw)
                self._handle_payload(peer_id, user_id, payload)
                return
            except Exception:
                pass

        text_lower = text.lower()

        if self._is_chat(peer_id):
            chat_id = self._peer_to_chat_id(peer_id)
            game = self._get_game(chat_id)

            if text_lower in {"/mafia", "мафия", "игра"}:
                self._send(peer_id, self._status_text(game), keyboard=self._build_lobby_keyboard())
                return
            if text_lower in {"/help", "помощь"}:
                self._send(peer_id, "Команды: мафия, войти, выйти, старт, статус, стоп")
                return
            if text_lower in {"войти", "join"}:
                self._add_player(game, user_id)
                self._send(peer_id, f"{self._mention(user_id)} в игре!")
                return
            if text_lower in {"выйти", "leave"}:
                if game["phase"] == PHASE_LOBBY:
                    self._remove_player(game, user_id)
                    self._send(peer_id, f"{self._mention(user_id)} вышел.")
                else:
                    self._player(game, user_id)["alive"] = False
                    self._send(peer_id, f"{self._mention(user_id)} покинул игру и считается мёртвым.")
                    self._check_win(game)
                return
            if text_lower in {"старт", "start"}:
                self._start_game(game, peer_id)
                return
            if text_lower in {"статус", "status"}:
                self._send(peer_id, self._status_text(game), keyboard=self._build_lobby_keyboard())
                return
            if text_lower in {"стоп", "stop"}:
                if self.admin_id and user_id != self.admin_id:
                    self._send(peer_id, "Только админ может стопнуть игру.")
                else:
                    game["phase"] = PHASE_ENDED
                    game["phase_end"] = 0
                    self._send(peer_id, "🛑 Игра остановлена.")
                return

        self._handle_text_action(peer_id, user_id, text_lower)
это же python, a можешь для js скинут пожалуйста ну или это js работат будет?
 
Янв
243
531
Продавец
JSON:
// mafia_module.js
const { Keyboard } = require("vk-io");

const ROLE_MAFIA = "мафия";
const ROLE_DETECTIVE = "комиссар";
const ROLE_DOCTOR = "доктор";
const ROLE_CIVIL = "мирный";

const PHASE_LOBBY = "lobby";
const PHASE_NIGHT = "night";
const PHASE_DAY = "day";
const PHASE_VOTE = "vote";
const PHASE_ENDED = "ended";

class MafiaModule {
  constructor(vk, {
    adminId = 0,
    nightSec = 60,
    daySec = 90,
    voteSec = 45,
    minPlayers = 3,
    maxPlayers = 999,
  } = {}) {
    this.vk = vk;
    this.adminId = adminId;
    this.nightSec = nightSec;
    this.daySec = daySec;
    this.voteSec = voteSec;
    this.minPlayers = minPlayers;
    this.maxPlayers = maxPlayers;

    this.games = new Map();
    this.nameCache = new Map();

    setInterval(() => this._tick(), 1000);
  }

  _isChat(peerId) {
    return peerId >= 2000000000;
  }

  _peerToChatId(peerId) {
    return peerId - 2000000000;
  }

  async _getName(userId) {
    if (this.nameCache.has(userId)) return this.nameCache.get(userId);
    try {
      const [info] = await this.vk.api.users.get({ user_ids: userId });
      const name = info.first_name || String(userId);
      this.nameCache.set(userId, name);
      return name;
    } catch {
      return String(userId);
    }
  }

  async _mention(userId) {
    const name = await this._getName(userId);
    return `[id${userId}|${name}]`;
  }

  async _send(peerId, text, keyboard = null) {
    const params = { peer_id: peerId, random_id: Date.now(), message: text };
    if (keyboard) params.keyboard = keyboard;
    await this.vk.api.messages.send(params);
  }

  _newGame(chatId) {
    return {
      chatId,
      phase: PHASE_LOBBY,
      players: {},
      phaseEnd: 0,
      night: {},
      dayVotes: {},
      round: 0,
    };
  }

  _getGame(chatId) {
    if (!this.games.has(chatId)) this.games.set(chatId, this._newGame(chatId));
    return this.games.get(chatId);
  }

  _player(game, userId) {
    return game.players[String(userId)];
  }

  _listPlayers(game, aliveOnly = false) {
    const ids = [];
    for (const [uid, p] of Object.entries(game.players)) {
      if (aliveOnly && !p.alive) continue;
      ids.push(Number(uid));
    }
    return ids;
  }

  async _addPlayer(game, userId) {
    if (game.players[String(userId)]) return;
    if (Object.keys(game.players).length >= this.maxPlayers) return;
    const name = await this._getName(userId);
    game.players[String(userId)] = { name, alive: true, role: null };
  }

  _removePlayer(game, userId) {
    delete game.players[String(userId)];
  }

  _assignRoles(game) {
    const ids = this._listPlayers(game);
    for (let i = ids.length - 1; i > 0; i--) {
      const j = Math.floor(Math.random() * (i + 1));
      [ids[i], ids[j]] = [ids[j], ids[i]];
    }
    const mafiaCount = Math.max(1, Math.floor(ids.length / 3));
    const mafiaIds = ids.slice(0, mafiaCount);
    const rest = ids.slice(mafiaCount);
    const detective = rest[0] || null;
    const doctor = rest[1] || null;

    for (const uid of ids) {
      let role = ROLE_CIVIL;
      if (mafiaIds.includes(uid)) role = ROLE_MAFIA;
      else if (uid === detective) role = ROLE_DETECTIVE;
      else if (uid === doctor) role = ROLE_DOCTOR;
      game.players[String(uid)].role = role;
    }
  }

  async _sendRoles(game) {
    const mafiaIds = this._listPlayers(game).filter(
      (uid) => this._player(game, uid).role === ROLE_MAFIA
    );
    const mafiaNames = (await Promise.all(mafiaIds.map((u) => this._mention(u)))).join(", ");
    for (const uid of this._listPlayers(game)) {
      const role = this._player(game, uid).role;
      let text = "🙂 Роль: МИРНЫЙ. Днём ищи мафию.";
      if (role === ROLE_MAFIA) text = `🕶 Роль: МАФИЯ\nСоюзники: ${mafiaNames}\nНочью выбирай жертву.`;
      if (role === ROLE_DETECTIVE) text = "🕵️ Роль: КОМИССАР. Ночью проверяй игроков.";
      if (role === ROLE_DOCTOR) text = "🩺 Роль: ДОКТОР. Ночью выбирай, кого спасти.";
      await this._send(uid, text);
    }
  }

  _buildLobbyKeyboard() {
    return Keyboard.builder()
      .textButton({ label: "✅ Войти", color: Keyboard.POSITIVE_COLOR, payload: { action: "join" } })
      .textButton({ label: "🚪 Выйти", color: Keyboard.NEGATIVE_COLOR, payload: { action: "leave" } })
      .row()
      .textButton({ label: "🚀 Старт", color: Keyboard.PRIMARY_COLOR, payload: { action: "start" } })
      .textButton({ label: "📊 Статус", color: Keyboard.SECONDARY_COLOR, payload: { action: "status" } })
      .row()
      .textButton({ label: "🛑 Стоп", color: Keyboard.NEGATIVE_COLOR, payload: { action: "stop" } })
      .inline(false)
      .oneTime(false)
      .build();
  }

  _buildTargetsKeyboard(targets, action, chatId) {
    if (targets.length === 0 || targets.length > 10) return null;
    const kb = Keyboard.builder().inline().oneTime();
    for (const uid of targets) {
      kb.textButton({
        label: String(uid),
        color: Keyboard.PRIMARY_COLOR,
        payload: { action, chatId, target: uid },
      });
    }
    return kb.build();
  }

  async _startGame(game, peerId) {
    if (game.phase !== PHASE_LOBBY) {
      await this._send(peerId, "Игра уже идёт.");
      return;
    }
    if (Object.keys(game.players).length < this.minPlayers) {
      await this._send(peerId, `Нужно минимум ${this.minPlayers} игроков.`);
      return;
    }
    this._assignRoles(game);
    await this._sendRoles(game);
    game.round = 1;
    await this._beginNight(game);
    await this._send(peerId, "🎬 Игра началась! Ночь наступает...");
  }

  async _beginNight(game) {
    game.phase = PHASE_NIGHT;
    game.phaseEnd = Date.now() + this.nightSec * 1000;
    game.night = { mafiaVotes: {}, doctor: null, detective: null };

    const chatPeer = game.chatId + 2000000000;
    await this._send(chatPeer, "🌙 Ночь. Все засыпают...");

    const alive = this._listPlayers(game, true);
    const mafiaIds = alive.filter((u) => this._player(game, u).role === ROLE_MAFIA);
    const targetable = alive.filter((u) => this._player(game, u).role !== ROLE_MAFIA);

    for (const uid of mafiaIds) {
      const kb = this._buildTargetsKeyboard(targetable, "kill", game.chatId);
      if (kb) await this._send(uid, "Выберите жертву:", kb);
      else await this._send(uid, "Выберите жертву: kill ID");
    }

    for (const uid of alive) {
      const role = this._player(game, uid).role;
      if (role === ROLE_DOCTOR) {
        const kb = this._buildTargetsKeyboard(alive, "heal", game.chatId);
        await this._send(uid, kb ? "Кого лечить?" : "Кого лечить? heal ID", kb);
      }
      if (role === ROLE_DETECTIVE) {
        const kb = this._buildTargetsKeyboard(alive, "check", game.chatId);
        await this._send(uid, kb ? "Кого проверить?" : "Кого проверить? check ID", kb);
      }
    }
  }

  async _resolveNight(game) {
    const alive = this._listPlayers(game, true);
    const mafiaVotes = game.night.mafiaVotes || {};
    const doctorTarget = game.night.doctor;
    const detectiveTarget = game.night.detective;

    let killTarget = null;
    const entries = Object.entries(mafiaVotes);
    if (entries.length > 0) {
      const maxVotes = Math.max(...entries.map(([, v]) => v));
      const top = entries.filter(([, v]) => v === maxVotes).map(([u]) => Number(u));
      killTarget = top[Math.floor(Math.random() * top.length)];
    }

    let killed = null;
    if (killTarget && alive.includes(killTarget) && killTarget !== doctorTarget) {
      this._player(game, killTarget).alive = false;
      killed = killTarget;
    }

    if (detectiveTarget && alive.includes(detectiveTarget)) {
      const role = this._player(game, detectiveTarget).role;
      const msg = role === ROLE_MAFIA ? "🔎 Проверка: МАФИЯ" : "🔎 Проверка: НЕ мафия";
      const detId = alive.find((u) => this._player(game, u).role === ROLE_DETECTIVE);
      if (detId) await this._send(detId, msg);
    }

    const chatPeer = game.chatId + 2000000000;
    if (killed) await this._send(chatPeer, `☀️ Утро. Ночью был убит: ${await this._mention(killed)}`);
    else await this._send(chatPeer, "☀️ Утро. Ночь прошла без жертв.");

    if (await this._checkWin(game)) return;

    game.phase = PHASE_DAY;
    game.phaseEnd = Date.now() + this.daySec * 1000;
    game.dayVotes = {};
    await this._send(chatPeer, "🗣 День. Обсуждаем. Скоро голосование.");
  }

  async _beginVote(game) {
    game.phase = PHASE_VOTE;
    game.phaseEnd = Date.now() + this.voteSec * 1000;
    game.dayVotes = {};

    const chatPeer = game.chatId + 2000000000;
    const alive = this._listPlayers(game, true);
    const names = await Promise.all(alive.map((u) => this._mention(u)));
    await this._send(chatPeer, "🗳 Голосование! Кого линчуем?\n" + names.join("\n"));

    const kb = this._buildTargetsKeyboard(alive, "vote", game.chatId);
    if (kb) await this._send(chatPeer, "Выберите голос кнопкой:", kb);
    else await this._send(chatPeer, "Напишите: vote ID");
  }

  async _resolveVote(game) {
    const votes = game.dayVotes || {};
    const alive = this._listPlayers(game, true);
    const counted = {};
    for (const [voter, target] of Object.entries(votes)) {
      if (!alive.includes(Number(voter))) continue;
      if (!alive.includes(target)) continue;
      counted[target] = (counted[target] || 0) + 1;
    }

    const chatPeer = game.chatId + 2000000000;
    const entries = Object.entries(counted);
    if (entries.length === 0) {
      await this._send(chatPeer, "Никого не линчуем.");
    } else {
      const maxVotes = Math.max(...entries.map(([, v]) => v));
      const top = entries.filter(([, v]) => v === maxVotes).map(([u]) => Number(u));
      const lynched = top[Math.floor(Math.random() * top.length)];
      this._player(game, lynched).alive = false;
      await this._send(chatPeer, `⚖️ Линчевали: ${await this._mention(lynched)}`);
    }

    if (await this._checkWin(game)) return;
    game.round += 1;
    await this._beginNight(game);
  }

  async _checkWin(game) {
    const alive = this._listPlayers(game, true);
    const mafia = alive.filter((u) => this._player(game, u).role === ROLE_MAFIA);
    const civ = alive.filter((u) => this._player(game, u).role !== ROLE_MAFIA);
    const chatPeer = game.chatId + 2000000000;

    if (mafia.length === 0) {
      await this._send(chatPeer, "🏆 Победа мирных!");
      game.phase = PHASE_ENDED;
      game.phaseEnd = 0;
      return true;
    }
    if (mafia.length >= civ.length) {
      await this._send(chatPeer, "😈 Победа мафии!");
      game.phase = PHASE_ENDED;
      game.phaseEnd = 0;
      return true;
    }
    return false;
  }

  async _handlePayload(peerId, userId, payload) {
    const action = payload?.action;
    const chatId = payload?.chatId;
    if (["join", "leave", "start", "status", "stop"].includes(action)) {
      if (!this._isChat(peerId)) return;
      const game = this._getGame(this._peerToChatId(peerId));
      if (action === "join") {
        await this._addPlayer(game, userId);
        await this._send(peerId, `${await this._mention(userId)} в игре!`);
      } else if (action === "leave") {
        if (game.phase === PHASE_LOBBY) {
          this._removePlayer(game, userId);
          await this._send(peerId, `${await this._mention(userId)} вышел.`);
        } else {
          this._player(game, userId).alive = false;
          await this._send(peerId, `${await this._mention(userId)} покинул игру и считается мёртвым.`);
          await this._checkWin(game);
        }
      } else if (action === "start") {
        await this._startGame(game, peerId);
      } else if (action === "status") {
        await this._send(peerId, await this._statusText(game), this._buildLobbyKeyboard());
      } else if (action === "stop") {
        if (this.adminId && userId !== this.adminId) {
          await this._send(peerId, "Только админ может стопнуть игру.");
        } else {
          game.phase = PHASE_ENDED;
          game.phaseEnd = 0;
          await this._send(peerId, "🛑 Игра остановлена.");
        }
      }
      return;
    }

    if (["kill", "heal", "check", "vote"].includes(action)) {
      if (!chatId) return;
      const game = this._getGame(Number(chatId));
      const target = payload?.target;
      if (target == null) return;
      await this._handleAction(game, userId, action, Number(target));
    }
  }

  async _handleAction(game, userId, action, target) {
    const player = this._player(game, userId);
    if (!player || !player.alive) return;

    if (game.phase === PHASE_NIGHT) {
      if (action === "kill" && player.role === ROLE_MAFIA) {
        game.night.mafiaVotes[String(target)] = (game.night.mafiaVotes[String(target)] || 0) + 1;
        await this._send(userId, "✅ Голос за убийство принят.");
      } else if (action === "heal" && player.role === ROLE_DOCTOR) {
        game.night.doctor = target;
        await this._send(userId, "✅ Лечение принято.");
      } else if (action === "check" && player.role === ROLE_DETECTIVE) {
        game.night.detective = target;
        await this._send(userId, "✅ Проверка принята.");
      }
    } else if (game.phase === PHASE_VOTE && action === "vote") {
      if (!this._player(game, target) || !this._player(game, target).alive) return;
      game.dayVotes[String(userId)] = target;
      const chatPeer = game.chatId + 2000000000;
      await this._send(chatPeer, `🗳 ${await this._mention(userId)} голосует.`);
    }
  }

  async _handleTextAction(peerId, userId, text) {
    const parts = text.trim().split(/\s+/);
    if (parts.length !== 2) return;
    const cmd = parts[0].toLowerCase();
    const targetStr = parts[1];
    if (!/^\d+$/.test(targetStr)) return;
    const target = Number(targetStr);
    if (!["kill", "heal", "check", "vote"].includes(cmd)) return;

    let chatId = null;
    if (this._isChat(peerId)) chatId = this._peerToChatId(peerId);
    else {
      for (const g of this.games.values()) {
        if (g.players[String(userId)]) {
          chatId = g.chatId;
          break;
        }
      }
    }
    if (!chatId) return;
    const game = this._getGame(chatId);
    await this._handleAction(game, userId, cmd, target);
  }

  async _statusText(game) {
    const players = [];
    for (const uid of this._listPlayers(game)) {
      const p = this._player(game, uid);
      const status = p.alive ? "✅" : "💀";
      players.push(`${status} ${await this._mention(uid)}`);
    }
    const phaseMap = {
      [PHASE_LOBBY]: "лобби",
      [PHASE_NIGHT]: "ночь",
      [PHASE_DAY]: "день",
      [PHASE_VOTE]: "голосование",
      [PHASE_ENDED]: "завершена",
    };
    return `🎮 Мафия\nФаза: ${phaseMap[game.phase] || game.phase}\nРаунд: ${game.round}\n\nИгроки (${Object.keys(game.players).length}):\n${players.join("\n")}`;
  }

  async _tick() {
    const now = Date.now();
    for (const game of this.games.values()) {
      if (!game.phaseEnd || now < game.phaseEnd) continue;
      if (game.phase === PHASE_NIGHT) await this._resolveNight(game);
      else if (game.phase === PHASE_DAY) await this._beginVote(game);
      else if (game.phase === PHASE_VOTE) await this._resolveVote(game);
    }
  }

  async handleMessage(ctx) {
    const peerId = ctx.peerId;
    const userId = ctx.senderId;
    const text = (ctx.text || "").trim();

    if (ctx.messagePayload) {
      await this._handlePayload(peerId, userId, ctx.messagePayload);
      return;
    }

    const t = text.toLowerCase();

    if (this._isChat(peerId)) {
      const game = this._getGame(this._peerToChatId(peerId));

      if (["/mafia", "мафия", "игра"].includes(t)) {
        await this._send(peerId, await this._statusText(game), this._buildLobbyKeyboard());
        return;
      }
      if (["/help", "помощь"].includes(t)) {
        await this._send(peerId, "Команды: мафия, войти, выйти, старт, статус, стоп");
        return;
      }
      if (["войти", "join"].includes(t)) {
        await this._addPlayer(game, userId);
        await this._send(peerId, `${await this._mention(userId)} в игре!`);
        return;
      }
      if (["выйти", "leave"].includes(t)) {
        if (game.phase === PHASE_LOBBY) {
          this._removePlayer(game, userId);
          await this._send(peerId, `${await this._mention(userId)} вышел.`);
        } else {
          this._player(game, userId).alive = false;
          await this._send(peerId, `${await this._mention(userId)} покинул игру и считается мёртвым.`);
          await this._checkWin(game);
        }
        return;
      }
      if (["старт", "start"].includes(t)) {
        await this._startGame(game, peerId);
        return;
      }
      if (["статус", "status"].includes(t)) {
        await this._send(peerId, await this._statusText(game), this._buildLobbyKeyboard());
        return;
      }
      if (["стоп", "stop"].includes(t)) {
        if (this.adminId && userId !== this.adminId) {
          await this._send(peerId, "Только админ может стопнуть игру.");
        } else {
          game.phase = PHASE_ENDED;
          game.phaseEnd = 0;
          await this._send(peerId, "🛑 Игра остановлена.");
        }
        return;
      }
    }

    await this._handleTextAction(peerId, userId, t);
  }
}

module.exports = { MafiaModule };
 
Сверху