feat: create minesweeper game with v2 components

Introduces a new button-based Minesweeper mini-game in cogs/fun/minesweeper.py and registers it in the help command. Updates bot status messages in bot.py. Improves hackban embed formatting and adds author info in cogs/moderation/hackban.py.
This commit is contained in:
neoarz
2025-09-15 17:20:59 -04:00
parent 134b11cb59
commit 953246688d
4 changed files with 210 additions and 12 deletions

2
bot.py
View File

@@ -193,7 +193,7 @@ class DiscordBot(commands.Bot):
""" """
Setup the game status task of the bot. Setup the game status task of the bot.
""" """
statuses = ["with you!", "with Krypton!", "with humans!"] statuses = ["with you!", "with neo!", "with humans!", "learning rust!"]
await self.change_presence(activity=discord.Game(random.choice(statuses))) await self.change_presence(activity=discord.Game(random.choice(statuses)))
@status_task.before_loop @status_task.before_loop

203
cogs/fun/minesweeper.py Normal file
View File

@@ -0,0 +1,203 @@
import random
from itertools import repeat
import discord
from discord.ext import commands
from discord.ext.commands import Context
class RowButton(discord.ui.Button):
def __init__(self, ctx, label, custom_id, bombs, board):
super().__init__(label=label, style=discord.ButtonStyle.grey, custom_id=custom_id)
self.ctx = ctx
self.bombs = bombs
self.board = board
async def callback(self, interaction):
assert self.view is not None
view: MsView = self.view
await interaction.response.defer()
if interaction.user.id != self.ctx.author.id:
return await interaction.followup.send(
"You cannot interact with these buttons.", ephemeral=True
)
b_id = self.custom_id
if int(b_id[5:]) in view.moves:
return await interaction.followup.send("That part is already taken.", ephemeral=True)
if int(b_id[5:]) in self.bombs:
await view.RevealBombs(b_id, view.board)
else:
count = []
rawpos = int(b_id[5:])
pos = view.GetBoardPos(rawpos)
def checkpos(count, rawpos, pos):
pos = view.GetBoardPos(rawpos)
if not rawpos - 1 in self.bombs or pos == 0:
count.append(rawpos - 1)
if not rawpos + 1 in self.bombs or pos == 4:
count.append(rawpos + 1)
if not rawpos - 6 in self.bombs or pos == 0:
count.append(rawpos - 6)
if not rawpos - 4 in self.bombs or pos == 4:
count.append(rawpos - 4)
if not rawpos + 6 in self.bombs or pos == 4:
count.append(rawpos + 6)
if not rawpos + 4 in self.bombs or pos == 0:
count.append(rawpos + 4)
if not rawpos - 5 in self.bombs:
count.append(rawpos - 5)
if not rawpos + 5 in self.bombs:
count.append(rawpos + 5)
return count
count = checkpos(count, rawpos, pos)
number = 8-len(count)
self.label = str(number) if number > 0 else "0"
self.style = discord.ButtonStyle.green
pos = int(b_id[5:])
view.board[view.GetBoardRow(pos)][
view.GetBoardPos(pos)
] = str(number) if number > 0 else "0"
view.moves.append(pos)
if len(view.moves) + len(self.bombs) == 25:
await interaction.edit_original_response(view=view)
await view.EndGame()
await interaction.edit_original_response(view=view)
class MsView(discord.ui.View):
def __init__(self, ctx, options, bombs, board):
super().__init__()
for i, op in enumerate(options):
self.add_item(RowButton(ctx, op, f"block{i}", bombs, board))
self.board = board
self.bombs = bombs
self.moves = []
self.ctx = ctx
self.message = None
async def EndGame(self):
embed = discord.Embed(
title="Minesweeper",
description="Game Ended. You won!",
color=0x00FF00
)
embed.set_author(name="Fun", icon_url="https://yes.nighty.works/raw/eW5lLm.webp")
await self.message.edit(embed=embed, view=self)
for button in self.children:
button.disabled = True
pos = int(button.custom_id[5:])
if pos in self.bombs:
button.label = "💣"
button.style = discord.ButtonStyle.red
self.board[self.GetBoardRow(pos)][self.GetBoardPos(pos)] = "💣"
def GetBoardRow(self, pos):
if pos in [0, 1, 2, 3, 4]:
return 0
if pos in [5, 6, 7, 8, 9]:
return 1
if pos in [10, 11, 12, 13, 14]:
return 2
if pos in [15, 16, 17, 18, 19]:
return 3
if pos in [20, 21, 22, 23, 24]:
return 4
return False
def GetBoardPos(self, pos):
if pos in [0, 1, 2, 3, 4]:
return pos
if pos in [5, 6, 7, 8, 9]:
for i, num in enumerate(range(5, 10)):
if pos == num:
return i
if pos in [10, 11, 12, 13, 14]:
for i, num in enumerate(range(10, 15)):
if pos == num:
return i
if pos in [15, 16, 17, 18, 19]:
for i, num in enumerate(range(15, 20)):
if pos == num:
return i
if pos in [20, 21, 22, 23, 24]:
for i, num in enumerate(range(20, 25)):
if pos == num:
return i
return False
async def RevealBombs(self, b_id, board):
bombemo = "💣"
embed = discord.Embed(
title="Minesweeper",
description="💥 BOOM! You hit a bomb! Game Over!",
color=0xE02B2B
)
embed.set_author(name="Fun", icon_url="https://yes.nighty.works/raw/eW5lLm.webp")
await self.message.edit(embed=embed, view=self)
for button in self.children:
button.disabled = True
if button.custom_id == b_id:
button.label = bombemo
button.style = discord.ButtonStyle.red
pos = int(b_id[5:])
self.board[self.GetBoardRow(pos)][self.GetBoardPos(pos)] = bombemo
for button in self.children:
if int(button.custom_id[5:]) in self.bombs:
button.label = bombemo
button.style = discord.ButtonStyle.red
pos = int(button.custom_id[5:])
self.board[self.GetBoardRow(pos)][
self.GetBoardPos(pos)
] = bombemo
class Minesweeper(commands.Cog, name="minesweeper"):
def __init__(self, bot) -> None:
self.bot = bot
@commands.hybrid_command(
name="minesweeper",
description="Play a buttoned minesweeper mini-game."
)
async def minesweeper(self, context: Context) -> None:
"""
Play a buttoned minesweeper mini-game.
:param context: The hybrid command context.
"""
board = [[" "] * 5 for _ in range(5)] # Unicode block character, usually doesnt show up in Discord or github, search up invisible character on google
bombs = 0
bombpositions = []
for x in repeat(None, random.randint(4, 11)):
random_index = random.randint(0, 24)
if random_index not in bombpositions and random_index not in [
0, 4, 20, 24
]:
bombpositions.append(random_index)
bombs += 1
def ExtractBlocks():
new_b = []
for x in board:
for y in x:
new_b.append(y)
return new_b
embed = discord.Embed(
title="Minesweeper",
description=f"💣 Total Bombs: `{len(bombpositions)}`\n\nClick the buttons to reveal the grid. Avoid the bombs!",
color=0x7289DA
)
embed.set_author(name="Fun", icon_url="https://yes.nighty.works/raw/eW5lLm.webp")
view = MsView(context, ExtractBlocks(), bombpositions, board)
message = await context.send(embed=embed, view=view)
view.message = message
async def setup(bot) -> None:
await bot.add_cog(Minesweeper(bot))

View File

@@ -49,6 +49,7 @@ class Help(commands.Cog, name="help"):
"coinflip": "fun", "coinflip": "fun",
"rps": "fun", "rps": "fun",
"8ball": "fun", "8ball": "fun",
"minesweeper": "fun",
"kick": "moderation", "kick": "moderation",
"ban": "moderation", "ban": "moderation",

View File

@@ -1,11 +1,3 @@
"""
Copyright © Krypton 2019-Present - https://github.com/kkrypt0nn (https://krypton.ninja)
Description:
🐍 A simple template to start to code your own and personalized Discord bot in Python
Version: 6.4.0
"""
import discord import discord
from discord import app_commands from discord import app_commands
from discord.ext import commands from discord.ext import commands
@@ -42,16 +34,18 @@ class HackBan(commands.Cog, name="hackban"):
int(user_id) int(user_id)
) )
embed = discord.Embed( embed = discord.Embed(
title="Ban",
description=f"**{user}** (ID: {user_id}) was banned by **{context.author}**!", description=f"**{user}** (ID: {user_id}) was banned by **{context.author}**!",
color=0xBEBEFE, color=0x7289DA,
) ).set_author(name="Moderation", icon_url="https://yes.nighty.works/raw/8VLDcg.webp")
embed.add_field(name="Reason:", value=reason) embed.add_field(name="Reason:", value=reason)
await context.send(embed=embed) await context.send(embed=embed)
except Exception: except Exception:
embed = discord.Embed( embed = discord.Embed(
title="Error!",
description="An error occurred while trying to ban the user. Make sure ID is an existing ID that belongs to a user.", description="An error occurred while trying to ban the user. Make sure ID is an existing ID that belongs to a user.",
color=0xE02B2B, color=0xE02B2B,
) ).set_author(name="Moderation", icon_url="https://yes.nighty.works/raw/8VLDcg.webp")
await context.send(embed=embed) await context.send(embed=embed)