mirror of
https://github.com/neoarz/Syntrel.git
synced 2025-12-25 03:40:11 +01:00
feat: baitbot
This commit is contained in:
@@ -30,6 +30,7 @@
|
|||||||
| sidestore | `help`, `refresh`, `code`, `crash`, `pairing`, `server`, `half`, `sparse`, `afc`, `udid` |
|
| sidestore | `help`, `refresh`, `code`, `crash`, `pairing`, `server`, `half`, `sparse`, `afc`, `udid` |
|
||||||
| idevice | `help`, `noapps`, `errorcode`, `developermode`, `mountddi` |
|
| idevice | `help`, `noapps`, `errorcode`, `developermode`, `mountddi` |
|
||||||
| melonx | `help`, `transfer`, `mods`, `gamecrash`, `requirements`, `error`, `26`, `legal` |
|
| 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` |
|
| miscellaneous | `keanu`, `labubu`, `piracy`, `tryitandsee`, `rickroll`, `dontasktoask`, `support`, `depart`, `docs` `sigma`, `duck`, `silly`, `color` |
|
||||||
| utilities | `translate`, `codepreview`, `dictionary` |
|
| utilities | `translate`, `codepreview`, `dictionary` |
|
||||||
| media | `download`, `mcquote`, `img2gif`, `tweety`, `tts` |
|
| media | `download`, `mcquote`, `img2gif`, `tweety`, `tts` |
|
||||||
|
|||||||
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}')
|
||||||
@@ -13,7 +13,7 @@ class Help(commands.Cog, name="help"):
|
|||||||
interaction: discord.Interaction,
|
interaction: discord.Interaction,
|
||||||
current: str,
|
current: str,
|
||||||
) -> list[app_commands.Choice[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 = []
|
suggestions = []
|
||||||
for category in categories:
|
for category in categories:
|
||||||
@@ -48,6 +48,7 @@ class Help(commands.Cog, name="help"):
|
|||||||
"sidestore": "sidestore",
|
"sidestore": "sidestore",
|
||||||
"utils": "utilities",
|
"utils": "utilities",
|
||||||
"utilities": "utilities",
|
"utilities": "utilities",
|
||||||
|
"events": "events",
|
||||||
|
|
||||||
"sync": "owner",
|
"sync": "owner",
|
||||||
"logs": "owner",
|
"logs": "owner",
|
||||||
@@ -69,7 +70,8 @@ class Help(commands.Cog, name="help"):
|
|||||||
"melonx": "MeloNX troubleshooting commands",
|
"melonx": "MeloNX troubleshooting commands",
|
||||||
"media": "Media commands",
|
"media": "Media commands",
|
||||||
"utilities": "Utility commands",
|
"utilities": "Utility commands",
|
||||||
"miscellaneous": "Miscellaneous commands"
|
"miscellaneous": "Miscellaneous commands",
|
||||||
|
"events": "Events commands"
|
||||||
}
|
}
|
||||||
|
|
||||||
if category is None:
|
if category is None:
|
||||||
|
|||||||
Reference in New Issue
Block a user