mirror of
https://github.com/neoarz/Syntrel.git
synced 2026-02-13 16:53:25 +01:00
Compare commits
8 Commits
minesweepe
...
baitbot
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ab3194dbdb | ||
|
|
7eb5a87da6 | ||
|
|
bc8f011d3f | ||
|
|
21e7c05536 | ||
|
|
2cd95f2b54 | ||
|
|
fc63f7c313 | ||
|
|
d50f66b2d3 | ||
|
|
a68272ee99 |
@@ -30,6 +30,7 @@
|
||||
| sidestore | `help`, `refresh`, `code`, `crash`, `pairing`, `server`, `half`, `sparse`, `afc`, `udid` |
|
||||
| idevice | `help`, `noapps`, `errorcode`, `developermode`, `mountddi` |
|
||||
| melonx | `help`, `transfer`, `mods`, `gamecrash`, `requirements`, `error`, `26`, `legal` |
|
||||
| events | `baitbot` |
|
||||
| miscellaneous | `keanu`, `labubu`, `piracy`, `tryitandsee`, `rickroll`, `dontasktoask`, `support`, `depart`, `docs` `sigma`, `duck`, `silly`, `color` |
|
||||
| utilities | `translate`, `codepreview`, `dictionary` |
|
||||
| media | `download`, `mcquote`, `img2gif`, `tweety`, `tts` |
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 610 KiB |
67
cogs/events/__init__.py
Normal file
67
cogs/events/__init__.py
Normal file
@@ -0,0 +1,67 @@
|
||||
import discord
|
||||
from discord import app_commands
|
||||
from discord.ext import commands
|
||||
from discord.ext.commands import Context
|
||||
|
||||
from .baitbot import baitbot_command, BaitBotListener, has_protected_role
|
||||
|
||||
|
||||
def _require_group_prefix(context: Context) -> bool:
|
||||
if getattr(context, "interaction", None):
|
||||
return True
|
||||
group = getattr(getattr(context, "cog", None), "qualified_name", "").lower()
|
||||
if not group:
|
||||
return True
|
||||
prefix = context.prefix or ""
|
||||
content = context.message.content.strip().lower()
|
||||
return content.startswith(f"{prefix}{group} ")
|
||||
|
||||
|
||||
@app_commands.allowed_contexts(guilds=True, dms=True, private_channels=True)
|
||||
@app_commands.allowed_installs(guilds=True, users=True)
|
||||
class Events(commands.GroupCog, name="events"):
|
||||
def __init__(self, bot) -> None:
|
||||
self.bot = bot
|
||||
super().__init__()
|
||||
|
||||
@commands.group(name="events", invoke_without_command=True)
|
||||
async def events_group(self, context: Context):
|
||||
embed = discord.Embed(
|
||||
title="Events Commands",
|
||||
description="Use `.events <subcommand>` or `/events <subcommand>`.",
|
||||
color=0x7289DA
|
||||
)
|
||||
embed.set_author(name="Events", icon_url="https://yes.nighty.works/raw/eW5lLm.webp")
|
||||
embed.add_field(name="Available", value="baitbot", inline=False)
|
||||
await context.send(embed=embed)
|
||||
|
||||
async def _invoke_hybrid(self, context: Context, name: str, **kwargs):
|
||||
command = self.bot.get_command(name)
|
||||
if command is not None:
|
||||
await context.invoke(command, **kwargs)
|
||||
else:
|
||||
await context.send(f"Unknown events command: {name}")
|
||||
|
||||
@events_group.command(name="baitbot")
|
||||
@has_protected_role()
|
||||
async def events_group_baitbot(self, context: Context):
|
||||
await self._invoke_hybrid(context, "baitbot")
|
||||
|
||||
@commands.check(_require_group_prefix)
|
||||
@has_protected_role()
|
||||
@commands.hybrid_command(
|
||||
name="baitbot",
|
||||
description="View bait bot configuration and status."
|
||||
)
|
||||
async def baitbot(self, context):
|
||||
return await baitbot_command()(self, context)
|
||||
|
||||
|
||||
async def setup(bot) -> None:
|
||||
cog = Events(bot)
|
||||
await bot.add_cog(cog)
|
||||
|
||||
listener = BaitBotListener(bot)
|
||||
await bot.add_cog(listener)
|
||||
|
||||
bot.logger.info("Loaded extension 'events.baitbot'")
|
||||
290
cogs/events/baitbot.py
Normal file
290
cogs/events/baitbot.py
Normal file
@@ -0,0 +1,290 @@
|
||||
import discord
|
||||
from discord.ext import commands
|
||||
from discord.ext.commands import Context
|
||||
import asyncio
|
||||
|
||||
# Make a pr to add your own server config here, you shouldn't need to touch the rest of the file, please fill in all the values for your own server
|
||||
BAIT_CONFIGS = {
|
||||
"SideStore": {
|
||||
"guild_id": 949183273383395328,
|
||||
"channel_ids": [
|
||||
1432134748062482586,
|
||||
1432204211009097819,
|
||||
],
|
||||
"protected_role_id": 959598279685963776,
|
||||
"log_channel_id": 1433532504647667823,
|
||||
},
|
||||
"neotest": {
|
||||
"guild_id": 1069946178659160076,
|
||||
"channel_ids": [
|
||||
1432175690270118012,
|
||||
1433987189670281278,
|
||||
1433988339031080991,
|
||||
],
|
||||
"protected_role_id": 1432165329483857940,
|
||||
"log_channel_id": 1433987853184139365,
|
||||
},
|
||||
"idevice": {
|
||||
"guild_id": 1329314147434758175,
|
||||
"channel_ids": [
|
||||
1434317669695492177,
|
||||
],
|
||||
"protected_role_id": 1333666918548111373,
|
||||
"log_channel_id": 1333673259446571022,
|
||||
},
|
||||
"melonx": {
|
||||
"guild_id": 1300369899704680479,
|
||||
"channel_ids": [
|
||||
1434327970679492830,
|
||||
],
|
||||
"protected_role_id": 1300372178138697758,
|
||||
"log_channel_id": 1300374786471366696,
|
||||
},
|
||||
}
|
||||
|
||||
BAN_REASON = 'Detected bot/scammer in bait channel'
|
||||
|
||||
def has_protected_role():
|
||||
async def predicate(context: Context):
|
||||
if not context.guild:
|
||||
context.bot.logger.warning(f'[BAITBOT] Unauthorized baitbot command attempt by {context.author} ({context.author.id}) in DMs')
|
||||
embed = discord.Embed(
|
||||
title="Permission Denied",
|
||||
description="You don't have permission to use this command.",
|
||||
color=0xE02B2B
|
||||
)
|
||||
embed.set_author(name="Events", icon_url="https://yes.nighty.works/raw/C8Hh6o.png")
|
||||
await context.send(embed=embed, ephemeral=True)
|
||||
return False
|
||||
|
||||
if not hasattr(context.author, 'roles'):
|
||||
context.bot.logger.warning(f'[BAITBOT] Unauthorized baitbot command attempt by {context.author} ({context.author.id}) in {context.guild.name} - no roles')
|
||||
embed = discord.Embed(
|
||||
title="Permission Denied",
|
||||
description="You don't have permission to use this command.",
|
||||
color=0xE02B2B
|
||||
)
|
||||
embed.set_author(name="Events", icon_url="https://yes.nighty.works/raw/C8Hh6o.png")
|
||||
await context.send(embed=embed, ephemeral=True)
|
||||
return False
|
||||
|
||||
for config in BAIT_CONFIGS.values():
|
||||
protected_role_id = config.get("protected_role_id")
|
||||
if protected_role_id:
|
||||
protected_role = context.guild.get_role(protected_role_id)
|
||||
if protected_role:
|
||||
for role in context.author.roles:
|
||||
if role.position >= protected_role.position and role.id != context.guild.default_role.id:
|
||||
return True
|
||||
|
||||
context.bot.logger.warning(f'[BAITBOT] Unauthorized baitbot command attempt by {context.author} ({context.author.id}) in {context.guild.name} - insufficient role permissions')
|
||||
embed = discord.Embed(
|
||||
title="Permission Denied",
|
||||
description="You don't have permission to use this command.",
|
||||
color=0xE02B2B
|
||||
)
|
||||
embed.set_author(name="Events", icon_url="https://yes.nighty.works/raw/C8Hh6o.png")
|
||||
await context.send(embed=embed, ephemeral=True)
|
||||
return False
|
||||
return commands.check(predicate)
|
||||
|
||||
def baitbot_command():
|
||||
async def wrapper(self, context: Context):
|
||||
embed = discord.Embed(
|
||||
title="Bait Bot",
|
||||
description="Bans people who post in a specific channel.",
|
||||
color=0x7289DA
|
||||
)
|
||||
embed.set_author(name="Events", icon_url="https://yes.nighty.works/raw/C8Hh6o.png")
|
||||
|
||||
found_config = False
|
||||
if BAIT_CONFIGS:
|
||||
for name, config in BAIT_CONFIGS.items():
|
||||
guild_id = config.get("guild_id")
|
||||
if context.guild and guild_id == context.guild.id:
|
||||
channel_ids = config.get("channel_ids", [])
|
||||
if not channel_ids:
|
||||
channel_id = config.get("channel_id")
|
||||
if channel_id:
|
||||
channel_ids = [channel_id]
|
||||
role_id = config.get("protected_role_id", "Not set")
|
||||
|
||||
channel_displays = []
|
||||
for channel_id in channel_ids:
|
||||
channel = context.guild.get_channel(channel_id)
|
||||
channel_display = f"<#{channel_id}> (`{channel_id}`)" if channel else f"`{channel_id}`"
|
||||
channel_displays.append(channel_display)
|
||||
|
||||
channels_text = "\n".join(channel_displays) if channel_displays else "Not set"
|
||||
|
||||
role = context.guild.get_role(role_id)
|
||||
role_display = f"<@&{role_id}> (`{role_id}`)" if role else f"`{role_id}`"
|
||||
|
||||
log_channel_id = config.get("log_channel_id")
|
||||
log_channel = None
|
||||
if log_channel_id:
|
||||
log_channel = context.guild.get_channel(log_channel_id)
|
||||
log_display = f"<#{log_channel_id}> (`{log_channel_id}`)" if log_channel else (f"`{log_channel_id}`" if log_channel_id else "Not set")
|
||||
|
||||
embed.add_field(
|
||||
name="\u200b",
|
||||
value=f"Channels:\n{channels_text}\n\nProtected Role:\n{role_display}\n\nLog Channel:\n{log_display}",
|
||||
inline=False
|
||||
)
|
||||
found_config = True
|
||||
|
||||
if not found_config:
|
||||
embed.add_field(
|
||||
name="No Configurations",
|
||||
value="No bait channels configured for this server",
|
||||
inline=False
|
||||
)
|
||||
|
||||
if context.guild and context.guild.icon:
|
||||
embed.set_thumbnail(url=context.guild.icon.url)
|
||||
|
||||
await context.send(embed=embed)
|
||||
return wrapper
|
||||
|
||||
class BaitBotListener(commands.Cog):
|
||||
def __init__(self, bot):
|
||||
self.bot = bot
|
||||
|
||||
@commands.Cog.listener()
|
||||
async def on_message(self, message: discord.Message):
|
||||
if message.guild is None:
|
||||
return
|
||||
|
||||
if message.author.bot:
|
||||
return
|
||||
|
||||
bait_config = None
|
||||
config_name = None
|
||||
for name, config in BAIT_CONFIGS.items():
|
||||
if message.guild.id != config.get("guild_id"):
|
||||
continue
|
||||
|
||||
channel_ids = config.get("channel_ids", [])
|
||||
if not channel_ids:
|
||||
channel_id = config.get("channel_id")
|
||||
if channel_id:
|
||||
channel_ids = [channel_id]
|
||||
|
||||
if message.channel.id in channel_ids:
|
||||
bait_config = config
|
||||
config_name = name
|
||||
break
|
||||
|
||||
if not bait_config:
|
||||
return
|
||||
|
||||
protected_role_id = bait_config.get("protected_role_id")
|
||||
is_protected = False
|
||||
if protected_role_id and hasattr(message.author, 'roles'):
|
||||
protected_role = message.guild.get_role(protected_role_id)
|
||||
if protected_role:
|
||||
for role in message.author.roles:
|
||||
if role.position >= protected_role.position and role.id != message.guild.default_role.id:
|
||||
self.bot.logger.info(f'[BAITBOT] Skipped banning {message.author} ({message.author.id}) in #{message.channel.name}: protected role ({role.name})')
|
||||
is_protected = True
|
||||
break
|
||||
|
||||
message_content = message.content if message.content else "*No text content*"
|
||||
message_attachments = message.attachments
|
||||
message_embeds = message.embeds
|
||||
|
||||
try:
|
||||
await message.delete()
|
||||
self.bot.logger.info(f'[BAITBOT] Deleted message from {message.author} in #{message.channel.name}')
|
||||
except Exception as e:
|
||||
self.bot.logger.warning(f'[BAITBOT] Could not delete message from {message.author}: {e}')
|
||||
banned = False
|
||||
if not is_protected:
|
||||
try:
|
||||
self.bot.logger.warning(f'[BAITBOT] Detected user in bait channel [{config_name}]: {message.author.name} ({message.author.id}) in #{message.channel.name}')
|
||||
|
||||
if not message.guild.me.guild_permissions.ban_members:
|
||||
self.bot.logger.error(f'[BAITBOT] No permission to ban members in {message.guild.name}')
|
||||
else:
|
||||
try:
|
||||
await message.author.ban(reason=BAN_REASON, delete_message_days=7)
|
||||
self.bot.logger.info(f'[BAITBOT] Banned {message.author.name} - deleted messages from last 7 days')
|
||||
banned = True
|
||||
except discord.Forbidden:
|
||||
self.bot.logger.error(f'[BAITBOT] Could not ban {message.author.name}: missing permissions')
|
||||
except Exception as e:
|
||||
self.bot.logger.error(f'[BAITBOT] Error banning {message.author.name}: {e}')
|
||||
|
||||
if banned:
|
||||
await asyncio.sleep(2)
|
||||
try:
|
||||
await message.guild.unban(message.author, reason="Auto-unban after cleanup")
|
||||
self.bot.logger.info(f'[BAITBOT] Unbanned {message.author.name} - cleanup complete')
|
||||
except Exception as e:
|
||||
self.bot.logger.error(f'[BAITBOT] Error unbanning {message.author.name}: {e}')
|
||||
except Exception as e:
|
||||
self.bot.logger.error(f'[BAITBOT] Error handling bait message: {e}')
|
||||
|
||||
log_channel_id = bait_config.get("log_channel_id")
|
||||
if log_channel_id:
|
||||
try:
|
||||
log_channel = self.bot.get_channel(log_channel_id)
|
||||
if log_channel:
|
||||
action_text = "Message deleted (user banned and unbanned)" if banned else "Message deleted (protected user)" if is_protected else "Message deleted"
|
||||
log_embed = discord.Embed(
|
||||
title="Bait Bot",
|
||||
description=action_text,
|
||||
color=0xE02B2B,
|
||||
timestamp=message.created_at
|
||||
)
|
||||
log_embed.set_author(name=str(message.author), icon_url=message.author.display_avatar.url)
|
||||
log_embed.add_field(name="User", value=message.author.mention, inline=True)
|
||||
log_embed.add_field(name="Channel", value=message.channel.mention, inline=True)
|
||||
|
||||
combined_content = []
|
||||
if message_content and message_content != "*No text content*":
|
||||
combined_content.append(message_content)
|
||||
|
||||
image_url = None
|
||||
if message_attachments:
|
||||
for attachment in message_attachments:
|
||||
if attachment.content_type and attachment.content_type.startswith("image/"):
|
||||
if not image_url:
|
||||
image_url = attachment.url
|
||||
combined_content.append(attachment.filename)
|
||||
|
||||
content_text = "\n".join(combined_content) if combined_content else "*No content*"
|
||||
if len(content_text) > 1000:
|
||||
content_text = content_text[:997] + "..."
|
||||
|
||||
log_embed.add_field(name="Content", value=f"```\n{content_text}\n```", inline=False)
|
||||
|
||||
if image_url:
|
||||
log_embed.set_image(url=image_url)
|
||||
|
||||
if message_embeds:
|
||||
embed_info = []
|
||||
for embed in message_embeds[:3]:
|
||||
embed_desc = f"**Embed:** {embed.title or 'Untitled'}\n"
|
||||
if embed.description:
|
||||
desc_text = embed.description[:200] + "..." if len(embed.description) > 200 else embed.description
|
||||
embed_desc += f"{desc_text}\n"
|
||||
if embed.url:
|
||||
embed_desc += f"[Link]({embed.url})"
|
||||
embed_info.append(embed_desc)
|
||||
if embed_info:
|
||||
log_embed.add_field(name="Embeds", value="\n\n".join(embed_info), inline=False)
|
||||
|
||||
log_embed.set_footer(text=f"Message ID: {message.id}")
|
||||
|
||||
try:
|
||||
await log_channel.send(embed=log_embed)
|
||||
self.bot.logger.info(f'[BAITBOT] Sent log to #{log_channel.name}')
|
||||
except discord.Forbidden:
|
||||
self.bot.logger.error(f'[BAITBOT] No permission to send log to #{log_channel.name}')
|
||||
except Exception as e:
|
||||
self.bot.logger.error(f'[BAITBOT] Error sending log: {e}')
|
||||
else:
|
||||
self.bot.logger.warning(f'[BAITBOT] Log channel {log_channel_id} not found')
|
||||
except Exception as e:
|
||||
self.bot.logger.error(f'[BAITBOT] Error handling log channel: {e}')
|
||||
@@ -54,8 +54,8 @@ class Fun(commands.GroupCog, name="fun"):
|
||||
await self._invoke_hybrid(context, "8ball", question=question)
|
||||
|
||||
@fun_group.command(name="minesweeper")
|
||||
async def fun_group_minesweeper(self, context: Context, opponent: discord.User = None):
|
||||
await self._invoke_hybrid(context, "minesweeper", opponent=opponent)
|
||||
async def fun_group_minesweeper(self, context: Context):
|
||||
await self._invoke_hybrid(context, "minesweeper")
|
||||
|
||||
@fun_group.command(name="randomfact")
|
||||
async def fun_group_randomfact(self, context: Context):
|
||||
@@ -86,11 +86,8 @@ class Fun(commands.GroupCog, name="fun"):
|
||||
name="minesweeper",
|
||||
description="Play a buttoned minesweeper mini-game."
|
||||
)
|
||||
@app_commands.describe(
|
||||
opponent="Optional user to play against in multiplayer mode."
|
||||
)
|
||||
async def minesweeper(self, context, opponent: discord.User = None):
|
||||
return await minesweeper_command()(self, context, opponent=opponent)
|
||||
async def minesweeper(self, context):
|
||||
return await minesweeper_command()(self, context)
|
||||
|
||||
@commands.check(_require_group_prefix)
|
||||
@commands.hybrid_command(name="randomfact", description="Get a random fact.")
|
||||
|
||||
@@ -1,82 +1,16 @@
|
||||
import random
|
||||
from itertools import repeat
|
||||
import discord
|
||||
from discord import app_commands
|
||||
from discord.ext import commands
|
||||
import asyncio
|
||||
import time
|
||||
|
||||
class ChallengeConfirmView(discord.ui.View):
|
||||
def __init__(self, context, challenger, challenged, bomb_count, board):
|
||||
super().__init__(timeout=60)
|
||||
self.context = context
|
||||
self.challenger = challenger
|
||||
self.challenged = challenged
|
||||
self.bomb_count = bomb_count
|
||||
self.board = board
|
||||
self.accepted = False
|
||||
|
||||
@discord.ui.button(label="Confirm", style=discord.ButtonStyle.grey, emoji="<:Check:1417250703407186051>")
|
||||
async def confirm_button(self, interaction: discord.Interaction, button: discord.ui.Button):
|
||||
if interaction.user.id != self.challenged.id:
|
||||
return await interaction.response.send_message("Only the challenged player can accept!", ephemeral=True)
|
||||
|
||||
self.accepted = True
|
||||
self.stop()
|
||||
|
||||
embed = discord.Embed(
|
||||
title="Challenge Accepted!",
|
||||
description=f"🎮 **{self.challenger.mention}** vs **{self.challenged.mention}**\n\n💣 Total Bombs: `{self.bomb_count}`\n\n{self.challenger.mention} goes first! Click the buttons to reveal the grid. Avoid the bombs!",
|
||||
color=0x00FF00
|
||||
)
|
||||
embed.set_author(name="Fun", icon_url="https://yes.nighty.works/raw/eW5lLm.webp")
|
||||
|
||||
view = MsView(self.context, self.ExtractBlocks(), self.bomb_count, self.board, self.challenged)
|
||||
message = await interaction.response.edit_message(embed=embed, view=view)
|
||||
view.message = message
|
||||
|
||||
@discord.ui.button(label="Cancel", style=discord.ButtonStyle.grey, emoji="<:Cross:1417250649514446849>")
|
||||
async def cancel_button(self, interaction: discord.Interaction, button: discord.ui.Button):
|
||||
if interaction.user.id != self.challenged.id:
|
||||
return await interaction.response.send_message("Only the challenged player can decline!", ephemeral=True)
|
||||
|
||||
embed = discord.Embed(
|
||||
title="Challenge Declined",
|
||||
description=f"<a:woeisnuke:1432138229628276927> {self.challenged.mention} declined the minesweeper challenge from {self.challenger.mention}",
|
||||
color=0xE02B2B
|
||||
)
|
||||
embed.set_author(name="Fun", icon_url="https://yes.nighty.works/raw/eW5lLm.webp")
|
||||
|
||||
await interaction.response.edit_message(embed=embed, view=None)
|
||||
self.stop()
|
||||
|
||||
async def on_timeout(self):
|
||||
embed = discord.Embed(
|
||||
title="Challenge Expired",
|
||||
description=f"⏰ {self.challenged.mention} didn't respond to the minesweeper challenge from {self.challenger.mention}",
|
||||
color=0xFFA500
|
||||
)
|
||||
embed.set_author(name="Fun", icon_url="https://yes.nighty.works/raw/eW5lLm.webp")
|
||||
|
||||
try:
|
||||
await self.message.edit(embed=embed, view=None)
|
||||
except:
|
||||
pass
|
||||
|
||||
def ExtractBlocks(self):
|
||||
new_b = []
|
||||
for x in self.board:
|
||||
for y in x:
|
||||
new_b.append(y)
|
||||
return new_b
|
||||
|
||||
class RowButton(discord.ui.Button):
|
||||
def __init__(self, ctx, label, custom_id, bombs, board, opponent=None):
|
||||
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
|
||||
self.opponent = opponent
|
||||
|
||||
async def callback(self, interaction):
|
||||
assert self.view is not None
|
||||
@@ -99,20 +33,10 @@ class RowButton(discord.ui.Button):
|
||||
return
|
||||
raise
|
||||
|
||||
if view.opponent:
|
||||
if interaction.user.id not in [self.ctx.author.id, view.opponent.id]:
|
||||
return await interaction.followup.send(
|
||||
"You cannot interact with these buttons.", ephemeral=True
|
||||
)
|
||||
if interaction.user.id != view.current_player.id:
|
||||
return await interaction.followup.send(
|
||||
f"It's {view.current_player.mention}'s turn!", ephemeral=True
|
||||
)
|
||||
else:
|
||||
if interaction.user.id != self.ctx.author.id:
|
||||
return await interaction.followup.send(
|
||||
"You cannot interact with these buttons.", ephemeral=True
|
||||
)
|
||||
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:
|
||||
@@ -123,7 +47,7 @@ class RowButton(discord.ui.Button):
|
||||
self.bombs = view.bombs
|
||||
|
||||
if int(b_id[5:]) in self.bombs:
|
||||
await view.RevealBombs(b_id, view.board, interaction)
|
||||
await view.RevealBombs(b_id, view.board)
|
||||
else:
|
||||
count = []
|
||||
rawpos = int(b_id[5:])
|
||||
@@ -158,24 +82,11 @@ class RowButton(discord.ui.Button):
|
||||
view.GetBoardPos(pos)
|
||||
] = str(number) if number > 0 else "0"
|
||||
view.moves.append(pos)
|
||||
|
||||
if view.opponent:
|
||||
if interaction.user.id == view.ctx.author.id:
|
||||
view.player1_score += 1
|
||||
else:
|
||||
view.player2_score += 1
|
||||
|
||||
if number == 0:
|
||||
await view.auto_reveal_safe_squares(rawpos, interaction)
|
||||
|
||||
if view.opponent:
|
||||
view.current_player = view.opponent if view.current_player.id == view.ctx.author.id else view.ctx.author
|
||||
|
||||
if len(view.moves) + len(self.bombs) == 25:
|
||||
await view.EndGame()
|
||||
else:
|
||||
try:
|
||||
await view.update_embed(interaction)
|
||||
await interaction.edit_original_response(view=view)
|
||||
except discord.errors.HTTPException as e:
|
||||
if e.status == 429:
|
||||
await asyncio.sleep(1)
|
||||
@@ -183,10 +94,10 @@ class RowButton(discord.ui.Button):
|
||||
raise
|
||||
|
||||
class MsView(discord.ui.View):
|
||||
def __init__(self, ctx, options, bomb_count, board, opponent=None):
|
||||
def __init__(self, ctx, options, bomb_count, board):
|
||||
super().__init__(timeout=300)
|
||||
for i, op in enumerate(options):
|
||||
self.add_item(RowButton(ctx, op, f"block{i}", [], board, opponent))
|
||||
self.add_item(RowButton(ctx, op, f"block{i}", [], board))
|
||||
self.board = board
|
||||
self.bombs = []
|
||||
self.bomb_count = bomb_count
|
||||
@@ -195,12 +106,9 @@ class MsView(discord.ui.View):
|
||||
self.ctx = ctx
|
||||
self.message = None
|
||||
self.last_interaction = 0
|
||||
self.opponent = opponent
|
||||
self.current_player = ctx.author
|
||||
self.player1_score = 0
|
||||
self.player2_score = 0
|
||||
|
||||
def generate_bombs(self, first_move_pos):
|
||||
"""Generate bombs excluding the first clicked position"""
|
||||
bombpositions = []
|
||||
excluded_positions = [0, 4, 20, 24, first_move_pos]
|
||||
|
||||
@@ -227,7 +135,7 @@ class MsView(discord.ui.View):
|
||||
)
|
||||
embed.set_author(name="Fun", icon_url="https://yes.nighty.works/raw/eW5lLm.webp")
|
||||
try:
|
||||
await interaction.edit_original_response(embed=embed, view=self)
|
||||
await self.message.edit(embed=embed, view=self)
|
||||
except:
|
||||
pass
|
||||
|
||||
@@ -240,24 +148,9 @@ class MsView(discord.ui.View):
|
||||
button.style = discord.ButtonStyle.red
|
||||
self.board[self.GetBoardRow(pos)][self.GetBoardPos(pos)] = "💣"
|
||||
|
||||
if self.opponent:
|
||||
if self.player1_score > self.player2_score:
|
||||
winner = self.ctx.author
|
||||
elif self.player2_score > self.player1_score:
|
||||
winner = self.opponent
|
||||
else:
|
||||
winner = None
|
||||
|
||||
if winner:
|
||||
description = f"🎉 **{winner.mention}** won!"
|
||||
else:
|
||||
description = f"🤝 It's a tie!"
|
||||
else:
|
||||
description = "Game Ended. You won!"
|
||||
|
||||
embed = discord.Embed(
|
||||
title="Minesweeper",
|
||||
description=description,
|
||||
description="Game Ended. You won!",
|
||||
color=0x00FF00
|
||||
)
|
||||
embed.set_author(name="Fun", icon_url="https://yes.nighty.works/raw/eW5lLm.webp")
|
||||
@@ -271,110 +164,6 @@ class MsView(discord.ui.View):
|
||||
raise
|
||||
self.stop()
|
||||
|
||||
async def update_embed(self, interaction):
|
||||
if self.opponent:
|
||||
embed = discord.Embed(
|
||||
title="Minesweeper - Multiplayer",
|
||||
description=f"💣 Total Bombs: `{self.bomb_count}`\n\n🎮 **Players:**\n{self.ctx.author.mention} vs {self.opponent.mention}\n\n**Current Turn:** {self.current_player.mention}",
|
||||
color=0x7289DA
|
||||
)
|
||||
embed.set_author(name="Fun", icon_url="https://yes.nighty.works/raw/eW5lLm.webp")
|
||||
else:
|
||||
embed = discord.Embed(
|
||||
title="Minesweeper",
|
||||
description=f"💣 Total Bombs: `{self.bomb_count}`\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")
|
||||
|
||||
await interaction.edit_original_response(embed=embed, view=self)
|
||||
|
||||
async def auto_reveal_safe_squares(self, center_pos, interaction):
|
||||
positions_to_check = [center_pos]
|
||||
revealed_positions = set()
|
||||
current_player_id = interaction.user.id
|
||||
|
||||
while positions_to_check:
|
||||
current_pos = positions_to_check.pop(0)
|
||||
if current_pos in revealed_positions:
|
||||
continue
|
||||
|
||||
revealed_positions.add(current_pos)
|
||||
|
||||
adjacent_positions = []
|
||||
pos = self.GetBoardPos(current_pos)
|
||||
|
||||
if pos > 0 and current_pos - 1 not in self.bombs:
|
||||
adjacent_positions.append(current_pos - 1)
|
||||
if pos < 4 and current_pos + 1 not in self.bombs:
|
||||
adjacent_positions.append(current_pos + 1)
|
||||
if current_pos >= 5 and current_pos - 5 not in self.bombs:
|
||||
adjacent_positions.append(current_pos - 5)
|
||||
if current_pos <= 19 and current_pos + 5 not in self.bombs:
|
||||
adjacent_positions.append(current_pos + 5)
|
||||
if pos > 0 and current_pos >= 5 and current_pos - 6 not in self.bombs:
|
||||
adjacent_positions.append(current_pos - 6)
|
||||
if pos < 4 and current_pos >= 5 and current_pos - 4 not in self.bombs:
|
||||
adjacent_positions.append(current_pos - 4)
|
||||
if pos > 0 and current_pos <= 19 and current_pos + 4 not in self.bombs:
|
||||
adjacent_positions.append(current_pos + 4)
|
||||
if pos < 4 and current_pos <= 19 and current_pos + 6 not in self.bombs:
|
||||
adjacent_positions.append(current_pos + 6)
|
||||
|
||||
for adj_pos in adjacent_positions:
|
||||
if adj_pos not in self.moves and adj_pos not in revealed_positions:
|
||||
adj_count = []
|
||||
adj_pos_obj = self.GetBoardPos(adj_pos)
|
||||
|
||||
def checkpos_adj(count, rawpos, pos):
|
||||
pos = self.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
|
||||
|
||||
adj_count = checkpos_adj(adj_count, adj_pos, adj_pos_obj)
|
||||
adj_number = 8 - len(adj_count)
|
||||
|
||||
for button in self.children:
|
||||
if int(button.custom_id[5:]) == adj_pos:
|
||||
button.label = str(adj_number) if adj_number > 0 else "0"
|
||||
button.style = discord.ButtonStyle.green
|
||||
break
|
||||
|
||||
self.board[self.GetBoardRow(adj_pos)][self.GetBoardPos(adj_pos)] = str(adj_number) if adj_number > 0 else "0"
|
||||
self.moves.append(adj_pos)
|
||||
|
||||
if self.opponent:
|
||||
if current_player_id == self.ctx.author.id:
|
||||
self.player1_score += 1
|
||||
else:
|
||||
self.player2_score += 1
|
||||
|
||||
if adj_number == 0:
|
||||
positions_to_check.append(adj_pos)
|
||||
|
||||
try:
|
||||
await self.update_embed(interaction)
|
||||
except discord.errors.HTTPException as e:
|
||||
if e.status == 429:
|
||||
await asyncio.sleep(1)
|
||||
else:
|
||||
raise
|
||||
|
||||
def GetBoardRow(self, pos):
|
||||
if pos in [0, 1, 2, 3, 4]:
|
||||
return 0
|
||||
@@ -409,7 +198,7 @@ class MsView(discord.ui.View):
|
||||
return i
|
||||
return False
|
||||
|
||||
async def RevealBombs(self, b_id, board, interaction):
|
||||
async def RevealBombs(self, b_id, board):
|
||||
bombemo = "💣"
|
||||
|
||||
for button in self.children:
|
||||
@@ -429,22 +218,15 @@ class MsView(discord.ui.View):
|
||||
self.GetBoardPos(pos)
|
||||
] = bombemo
|
||||
|
||||
if self.opponent:
|
||||
loser = self.ctx.author if interaction.user.id == self.ctx.author.id else self.opponent
|
||||
winner = self.opponent if loser.id == self.ctx.author.id else self.ctx.author
|
||||
description = f"💥 BOOM! {loser.mention} hit a bomb!\n🎉 **{winner.mention}** wins!"
|
||||
else:
|
||||
description = f"💥 BOOM! You hit a bomb. Game Over!\n-# gg {self.ctx.author.mention}"
|
||||
|
||||
embed = discord.Embed(
|
||||
title="Minesweeper",
|
||||
description=description,
|
||||
description=f"💥 BOOM! You hit a bomb. Game Over!\n-# gg {self.ctx.author.mention}",
|
||||
color=0xE02B2B
|
||||
)
|
||||
embed.set_author(name="Fun", icon_url="https://yes.nighty.works/raw/eW5lLm.webp")
|
||||
|
||||
try:
|
||||
await interaction.edit_original_response(embed=embed, view=self)
|
||||
await self.message.edit(embed=embed, view=self)
|
||||
except discord.errors.HTTPException as e:
|
||||
if e.status == 429:
|
||||
await asyncio.sleep(1)
|
||||
@@ -457,10 +239,7 @@ def minesweeper_command():
|
||||
name="minesweeper",
|
||||
description="Play a buttoned minesweeper mini-game."
|
||||
)
|
||||
@app_commands.describe(
|
||||
opponent="Optional user to play against in multiplayer mode."
|
||||
)
|
||||
async def minesweeper(self, context, opponent: discord.User = None):
|
||||
async def minesweeper(self, context):
|
||||
board = [["឵឵ "] * 5 for _ in range(5)]
|
||||
bomb_count = random.randint(4, 11)
|
||||
|
||||
@@ -471,36 +250,15 @@ def minesweeper_command():
|
||||
new_b.append(y)
|
||||
return new_b
|
||||
|
||||
if opponent:
|
||||
if opponent.id == context.author.id:
|
||||
embed = discord.Embed(
|
||||
title="Error!",
|
||||
description="You cannot play against yourself!",
|
||||
color=0xE02B2B
|
||||
)
|
||||
embed.set_author(name="Fun", icon_url="https://yes.nighty.works/raw/eW5lLm.webp")
|
||||
return await context.send(embed=embed, ephemeral=True)
|
||||
|
||||
embed = discord.Embed(
|
||||
title="Minesweeper Challenge",
|
||||
description=f"🎮 **{opponent.mention}**, you are challenged by **{context.author.mention}** to a round of Minesweeper!\n\n💣 Total Bombs: `{bomb_count}`",
|
||||
color=0x7289DA
|
||||
)
|
||||
embed.set_author(name="Fun", icon_url="https://yes.nighty.works/raw/eW5lLm.webp")
|
||||
|
||||
view = ChallengeConfirmView(context, context.author, opponent, bomb_count, board)
|
||||
message = await context.send(embed=embed, view=view)
|
||||
view.message = message
|
||||
else:
|
||||
embed = discord.Embed(
|
||||
title="Minesweeper",
|
||||
description=f"💣 Total Bombs: `{bomb_count}`\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(), bomb_count, board, opponent)
|
||||
message = await context.send(embed=embed, view=view)
|
||||
view.message = message
|
||||
embed = discord.Embed(
|
||||
title="Minesweeper",
|
||||
description=f"💣 Total Bombs: `{bomb_count}`\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(), bomb_count, board)
|
||||
message = await context.send(embed=embed, view=view)
|
||||
view.message = message
|
||||
|
||||
return minesweeper
|
||||
|
||||
@@ -13,7 +13,7 @@ class Help(commands.Cog, name="help"):
|
||||
interaction: discord.Interaction,
|
||||
current: str,
|
||||
) -> list[app_commands.Choice[str]]:
|
||||
categories = ["general", "fun", "moderation", "owner", "sidestore", "idevice", "melonx", "media", "miscellaneous", "utilities"]
|
||||
categories = ["general", "fun", "moderation", "owner", "sidestore", "idevice", "melonx", "media", "miscellaneous", "utilities", "events"]
|
||||
|
||||
suggestions = []
|
||||
for category in categories:
|
||||
@@ -48,6 +48,7 @@ class Help(commands.Cog, name="help"):
|
||||
"sidestore": "sidestore",
|
||||
"utils": "utilities",
|
||||
"utilities": "utilities",
|
||||
"events": "events",
|
||||
|
||||
"sync": "owner",
|
||||
"logs": "owner",
|
||||
@@ -69,7 +70,8 @@ class Help(commands.Cog, name="help"):
|
||||
"melonx": "MeloNX troubleshooting commands",
|
||||
"media": "Media commands",
|
||||
"utilities": "Utility commands",
|
||||
"miscellaneous": "Miscellaneous commands"
|
||||
"miscellaneous": "Miscellaneous commands",
|
||||
"events": "Events commands"
|
||||
}
|
||||
|
||||
if category is None:
|
||||
|
||||
Reference in New Issue
Block a user