Some checks failed
CD / Deploy to VPS (push) Has been cancelled
212 lines
8.0 KiB
Python
212 lines
8.0 KiB
Python
import asyncio
|
|
import datetime
|
|
import random
|
|
|
|
import discord
|
|
from discord import option
|
|
from discord.ext import commands
|
|
|
|
|
|
class RouletteCog(commands.Cog):
|
|
def __init__(self, bot: discord.Bot):
|
|
self.bot = bot
|
|
self.cooldown = 60 * 3
|
|
self.active_games = {}
|
|
|
|
def _build_view(self, game: dict) -> discord.ui.View:
|
|
view = discord.ui.View(timeout=None)
|
|
|
|
if game["status"] == "pending":
|
|
async def accept_callback(interaction: discord.Interaction):
|
|
if interaction.user.id != game["target"].id:
|
|
await interaction.response.send_message(
|
|
"Only the challenged member can accept this duel.",
|
|
ephemeral=True,
|
|
)
|
|
return
|
|
|
|
game["status"] = "ready"
|
|
game["current_player"] = game["author"]
|
|
|
|
if game.get("message"):
|
|
await game["message"].edit(
|
|
content=(
|
|
f"{game['author'].mention} challenged {game['target'].mention} to a Russian roulette duel.\n"
|
|
f"✅ {game['target'].mention} accepted. {game['current_player'].mention}, click Fire!"
|
|
),
|
|
view=self._build_view(game),
|
|
)
|
|
|
|
await interaction.response.defer()
|
|
|
|
async def decline_callback(interaction: discord.Interaction):
|
|
if interaction.user.id != game["target"].id:
|
|
await interaction.response.send_message(
|
|
"Only the challenged member can decline this duel.",
|
|
ephemeral=True,
|
|
)
|
|
return
|
|
|
|
game["status"] = "declined"
|
|
self.active_games.pop((game["guild_id"], game["author"].id, game["target"].id), None)
|
|
|
|
if game.get("message"):
|
|
await game["message"].edit(
|
|
content=f"{game['target'].mention} declined the challenge.",
|
|
view=None,
|
|
)
|
|
|
|
await interaction.response.defer()
|
|
|
|
accept_button = discord.ui.Button(label="Accept", style=discord.ButtonStyle.green)
|
|
accept_button.callback = accept_callback
|
|
view.add_item(accept_button)
|
|
|
|
decline_button = discord.ui.Button(label="Decline", style=discord.ButtonStyle.red)
|
|
decline_button.callback = decline_callback
|
|
view.add_item(decline_button)
|
|
|
|
elif game["status"] in {"ready", "playing"}:
|
|
async def fire_callback(interaction: discord.Interaction):
|
|
if interaction.user.id != game["current_player"].id:
|
|
await interaction.response.send_message(
|
|
f"Wait for {game['current_player'].mention} to fire.",
|
|
ephemeral=True,
|
|
)
|
|
return
|
|
|
|
if interaction.user.id in game["fired"]:
|
|
await interaction.response.send_message(
|
|
"You already fired once. Only one shot per player is allowed.",
|
|
ephemeral=True,
|
|
)
|
|
return
|
|
|
|
game["fired"].add(interaction.user.id)
|
|
is_shot = random.randint(1, 6) == 1
|
|
|
|
if is_shot:
|
|
game["status"] = "finished"
|
|
self.active_games.pop((game["guild_id"], game["author"].id, game["target"].id), None)
|
|
|
|
await self._mute_member(interaction.user, 60)
|
|
|
|
if game.get("message"):
|
|
await game["message"].edit(
|
|
content=(
|
|
f"💥 {interaction.user.mention} pulled the trigger and got shot.\n"
|
|
f"They were muted for 60 seconds."
|
|
),
|
|
view=None,
|
|
)
|
|
else:
|
|
if len(game["fired"]) >= 2:
|
|
game["status"] = "finished"
|
|
self.active_games.pop((game["guild_id"], game["author"].id, game["target"].id), None)
|
|
|
|
if game.get("message"):
|
|
await game["message"].edit(
|
|
content=(
|
|
f"🔫 {interaction.user.mention} survived and both players have fired once.\n"
|
|
f"No one was shot. The duel ends in a draw."
|
|
),
|
|
view=None,
|
|
)
|
|
else:
|
|
game["status"] = "playing"
|
|
game["current_player"] = game["target"] if interaction.user.id == game["author"].id else game["author"]
|
|
|
|
if game.get("message"):
|
|
await game["message"].edit(
|
|
content=(
|
|
f"🔫 {interaction.user.mention} survived.\n"
|
|
f"Next turn: {game['current_player'].mention}"
|
|
),
|
|
view=self._build_view(game),
|
|
)
|
|
|
|
await interaction.response.defer()
|
|
|
|
fire_button = discord.ui.Button(label="Fire", style=discord.ButtonStyle.danger)
|
|
fire_button.callback = fire_callback
|
|
view.add_item(fire_button)
|
|
|
|
return view
|
|
|
|
async def _mute_member(self, member: discord.Member, seconds: int = 60):
|
|
until = discord.utils.utcnow() + datetime.timedelta(seconds=seconds)
|
|
try:
|
|
await member.timeout(until=until, reason="Russian roulette loss")
|
|
except AttributeError:
|
|
await member.edit(timed_out_until=until, reason="Russian roulette loss")
|
|
|
|
@commands.slash_command(
|
|
name="roulette",
|
|
description="Challenge other server members. The loser is getting muted for 1 minute",
|
|
)
|
|
@option(
|
|
name="target",
|
|
description="The member to challenge",
|
|
input_type=discord.Member,
|
|
required=True,
|
|
)
|
|
async def roulette(
|
|
self,
|
|
ctx: discord.ApplicationContext,
|
|
target: discord.Member,
|
|
):
|
|
author = ctx.author
|
|
self_test = author.id == target.id
|
|
|
|
if self_test:
|
|
target = author
|
|
|
|
game = {
|
|
"author": author,
|
|
"target": target,
|
|
"status": "pending",
|
|
"current_player": None,
|
|
"guild_id": ctx.guild_id,
|
|
"message": None,
|
|
"fired": set(),
|
|
}
|
|
|
|
self.active_games[(ctx.guild_id, author.id, target.id)] = game
|
|
|
|
await ctx.send_response(
|
|
f"{author.mention} challenged {target.mention} to a Russian roulette duel.\n"
|
|
f"The target has 60 seconds to accept or decline.",
|
|
view=self._build_view(game),
|
|
)
|
|
|
|
if self_test:
|
|
await ctx.send_followup(
|
|
"**Self-challenge enabled for testing.**\n"
|
|
"The game will start immediately and you can test the flow.",
|
|
ephemeral=True,
|
|
)
|
|
|
|
try:
|
|
game["message"] = await ctx.interaction.original_response()
|
|
except Exception:
|
|
game["message"] = None
|
|
|
|
async def timeout_handler():
|
|
try:
|
|
await asyncio.sleep(60)
|
|
except asyncio.CancelledError:
|
|
return
|
|
|
|
if game.get("status") == "pending":
|
|
game["status"] = "timeout"
|
|
self.active_games.pop((ctx.guild_id, author.id, target.id), None)
|
|
|
|
await self._mute_member(target, 60)
|
|
|
|
if game.get("message"):
|
|
await game["message"].edit(
|
|
content=f"⏰ {target.mention} did not respond in time and was muted for 60 seconds.",
|
|
view=None,
|
|
)
|
|
|
|
asyncio.create_task(timeout_handler()) |