2025-10-26 21:09:57 -04:00
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
BAIT_CONFIGS = {
" SideStore " : {
" guild_id " : 949183273383395328 ,
2025-10-31 21:41:36 -04:00
" channel_ids " : [
1432134748062482586 ,
1432204211009097819 ,
] ,
2025-10-26 21:33:51 -04:00
" protected_role_id " : 959598279685963776 ,
2025-10-31 21:41:36 -04:00
" log_channel_id " : 1433532504647667823 ,
2025-10-26 21:09:57 -04:00
} ,
" neotest " : {
" guild_id " : 1069946178659160076 ,
2025-10-31 21:41:36 -04:00
" channel_ids " : [
1432175690270118012 ,
1433987189670281278 ,
1433988339031080991 ,
] ,
2025-10-26 21:09:57 -04:00
" protected_role_id " : 1432165329483857940 ,
2025-10-31 21:41:36 -04:00
" log_channel_id " : 1433987853184139365 ,
2025-10-26 21:09:57 -04:00
} ,
}
BAN_REASON = ' Detected bot/scammer in bait channel '
def has_protected_role ( ) :
async def predicate ( context : Context ) :
if not context . guild :
2025-10-26 21:29:24 -04:00
context . bot . logger . warning ( f ' [BAITBOT] Unauthorized baitbot command attempt by { context . author } ( { context . author . id } ) in DMs ' )
2025-10-26 21:09:57 -04:00
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 ' ) :
2025-10-26 21:29:24 -04:00
context . bot . logger . warning ( f ' [BAITBOT] Unauthorized baitbot command attempt by { context . author } ( { context . author . id } ) in { context . guild . name } - no roles ' )
2025-10-26 21:09:57 -04:00
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
2025-10-26 21:29:24 -04:00
context . bot . logger . warning ( f ' [BAITBOT] Unauthorized baitbot command attempt by { context . author } ( { context . author . id } ) in { context . guild . name } - insufficient role permissions ' )
2025-10-26 21:09:57 -04:00
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 (
2025-10-26 21:29:24 -04:00
title = " Bait Bot " ,
2025-10-31 21:41:36 -04:00
description = " Bans people who post in a specific channel. " ,
2025-10-26 21:09:57 -04:00
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 :
2025-10-31 21:41:36 -04:00
channel_ids = config . get ( " channel_ids " , [ ] )
if not channel_ids :
channel_id = config . get ( " channel_id " )
if channel_id :
channel_ids = [ channel_id ]
2025-10-26 21:09:57 -04:00
role_id = config . get ( " protected_role_id " , " Not set " )
2025-10-31 21:41:36 -04:00
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 "
2025-10-26 21:09:57 -04:00
role = context . guild . get_role ( role_id )
role_display = f " <@& { role_id } > (` { role_id } `) " if role else f " ` { role_id } ` "
2025-10-31 21:41:36 -04:00
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 " )
2025-10-26 21:09:57 -04:00
embed . add_field (
2025-10-31 21:41:36 -04:00
name = " \u200b " ,
value = f " Channels: \n { channels_text } \n \n Protected Role: \n { role_display } \n \n Log Channel: \n { log_display } " ,
2025-10-26 21:09:57 -04:00
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
)
2025-10-31 21:41:36 -04:00
if context . guild and context . guild . icon :
embed . set_thumbnail ( url = context . guild . icon . url )
2025-10-26 21:09:57 -04:00
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 ( ) :
2025-10-31 21:41:36 -04:00
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 :
2025-10-26 21:09:57 -04:00
bait_config = config
config_name = name
break
if not bait_config :
return
protected_role_id = bait_config . get ( " protected_role_id " )
2025-10-26 21:20:25 -04:00
is_protected = False
2025-10-26 21:09:57 -04:00
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 :
2025-10-26 21:20:25 -04:00
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
2025-10-31 21:41:36 -04:00
message_content = message . content if message . content else " *No text content* "
message_attachments = message . attachments
message_embeds = message . embeds
2025-10-26 21:20:25 -04:00
try :
await message . delete ( )
2025-10-26 21:29:24 -04:00
self . bot . logger . info ( f ' [BAITBOT] Deleted message from { message . author } in # { message . channel . name } ' )
2025-10-26 21:20:25 -04:00
except Exception as e :
2025-10-26 21:29:24 -04:00
self . bot . logger . warning ( f ' [BAITBOT] Could not delete message from { message . author } : { e } ' )
2025-10-31 21:41:36 -04:00
banned = False
if not is_protected :
2025-10-26 21:20:25 -04:00
try :
2025-10-31 21:41:36 -04:00
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 } ' )
2025-10-26 21:20:25 -04:00
except Exception as e :
2025-10-31 21:41:36 -04:00
self . bot . logger . error ( f ' [BAITBOT] Error handling bait message: { e } ' )
log_channel_id = bait_config . get ( " log_channel_id " )
if log_channel_id :
2025-10-26 21:20:25 -04:00
try :
2025-10-31 21:41:36 -04:00
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 ' )
2025-10-26 21:20:25 -04:00
except Exception as e :
2025-10-31 21:41:36 -04:00
self . bot . logger . error ( f ' [BAITBOT] Error handling log channel: { e } ' )