import discord from discord.ext import commands import os import re import random from datetime import datetime import pytz from dotenv import load_dotenv from slack_sdk import WebClient from slack_sdk.errors import SlackApiError load_dotenv() intents = discord.Intents.default() intents.message_content = True bot = commands.Bot(command_prefix='!', intents=intents) # Initialize Slack client slack_client = WebClient(token=os.getenv('SLACK_BOT_TOKEN')) SLACK_CHANNEL = os.getenv('SLACK_CHANNEL', '#discord-bot-reports') # Channel IDs to monitor for bug reports MONITORED_CHANNELS = [ 1082775596767117312, # bugs-web 1258072243963957288, # bugs-ios 1314280428181520435 # bugs-android ] # Test channel - always respond here TEST_CHANNEL_ID = 1417577605980356792 # Bug-related keywords to detect BUG_KEYWORDS = [ 'bug', 'error' ] # Moderator role name MODERATOR_ROLE = "Moderator" @bot.event async def on_ready(): print(f'{bot.user} has connected to Discord!') def detect_bug_complaint(message_content): """Check if message contains bug-related keywords""" message_lower = message_content.lower() return any(keyword in message_lower for keyword in BUG_KEYWORDS) class BugReportView(discord.ui.View): def __init__(self, original_message): super().__init__(timeout=3600) # 1 hour timeout self.original_message = original_message self.bot_response = None # Will store the bot's response message self.slack_message_ts = None # Will store Slack message timestamp for editing self.slack_channel_id = None # Will store the actual channel ID used for Slack @discord.ui.button(label='🚨 Ping Moderator for Help', style=discord.ButtonStyle.secondary, emoji='🚨') async def ping_moderator(self, interaction: discord.Interaction, button: discord.ui.Button): # Find the moderator role moderator_role = discord.utils.get(interaction.guild.roles, name=MODERATOR_ROLE) if not moderator_role: await interaction.response.send_message( "❌ Moderator role not found. Please contact support directly.", ephemeral=True ) return # Create compact moderator ping embed mod_embed = discord.Embed( description=f"🚨 User assistance requested for a potential bug report! [Jump to message]({self.original_message.jump_url})", color=0xff9500 ) # Send as a regular message to trigger notifications properly await interaction.response.defer() # Acknowledge the interaction await interaction.channel.send( f"{moderator_role.mention}", embed=mod_embed ) # Disable the button after use and update the original message button.disabled = True button.label = "✅ Moderator Notified" button.style = discord.ButtonStyle.success # Update the original bot response to show the disabled button if self.bot_response: try: await self.bot_response.edit(view=self) except Exception as e: print(f"Could not update original message: {e}") # Update Slack message to show moderator has been notified if self.slack_message_ts: await self.update_slack_message_moderator_status(True) async def update_slack_message_moderator_status(self, moderator_notified): """Update the Slack message to show moderator notification status""" try: # Get channel name mapping channel_names = { 1082775596767117312: "bugs-web", 1258072243963957288: "bugs-ios", 1314280428181520435: "bugs-android", 1417577605980356792: "test-channel" } channel_name = channel_names.get(self.original_message.channel.id, f"#{self.original_message.channel.name}") # Create updated Slack message with moderator status slack_message = { "text": "🐛 New Bug Report Detected in Discord", "blocks": [ { "type": "header", "text": { "type": "plain_text", "text": "🐛 Bug Report Detected" } }, { "type": "section", "fields": [ { "type": "mrkdwn", "text": f"*User:* {self.original_message.author.display_name} (@{self.original_message.author.name})" }, { "type": "mrkdwn", "text": f"*Channel:* {channel_name}" }, { "type": "mrkdwn", "text": f"*Server:* {self.original_message.guild.name}" }, { "type": "mrkdwn", "text": f"*Time:* {self.original_message.created_at.replace(tzinfo=pytz.UTC).astimezone(pytz.timezone('US/Eastern')).strftime('%Y-%m-%d %H:%M:%S EST')}" }, { "type": "mrkdwn", "text": f"*Moderator Notified?* {'✅ Yes' if moderator_notified else '❌ No'}" } ] }, { "type": "section", "text": { "type": "mrkdwn", "text": f"*Message:*\n```{self.original_message.content}```" } }, { "type": "section", "text": { "type": "mrkdwn", "text": f"*Discord Link:* " } } ] } # Update the Slack message using the stored channel ID slack_client.chat_update( channel=self.slack_channel_id or SLACK_CHANNEL, ts=self.slack_message_ts, **slack_message ) except Exception as e: print(f"Error updating Slack message: {e}") async def send_slack_notification(message, view): """Send bug report notification to Slack""" try: # Get channel name mapping channel_names = { 1082775596767117312: "bugs-web", 1258072243963957288: "bugs-ios", 1314280428181520435: "bugs-android", 1417577605980356792: "test-channel" } channel_name = channel_names.get(message.channel.id, f"#{message.channel.name}") # Create Slack message slack_message = { "text": "🐛 New Bug Report Detected in Discord", "blocks": [ { "type": "header", "text": { "type": "plain_text", "text": "🐛 Bug Report Detected" } }, { "type": "section", "fields": [ { "type": "mrkdwn", "text": f"*User:* {message.author.display_name} (@{message.author.name})" }, { "type": "mrkdwn", "text": f"*Channel:* {channel_name}" }, { "type": "mrkdwn", "text": f"*Server:* {message.guild.name}" }, { "type": "mrkdwn", "text": f"*Time:* {message.created_at.replace(tzinfo=pytz.UTC).astimezone(pytz.timezone('US/Eastern')).strftime('%Y-%m-%d %H:%M:%S EST')}" }, { "type": "mrkdwn", "text": f"*Moderator Notified?* ❌ No" } ] }, { "type": "section", "text": { "type": "mrkdwn", "text": f"*Message:*\n```{message.content}```" } }, { "type": "section", "text": { "type": "mrkdwn", "text": f"*Discord Link:* " } } ] } # Send to Slack response = slack_client.chat_postMessage( channel=SLACK_CHANNEL, unfurl_links=False, unfurl_media=False, **slack_message ) # Store the message timestamp and channel ID in the view for later updates view.slack_message_ts = response['ts'] view.slack_channel_id = response['channel'] print(f"Slack notification sent successfully: {response['ts']}") except SlackApiError as e: print(f"Error sending Slack notification: {e.response['error']}") except Exception as e: print(f"Unexpected error sending Slack notification: {e}") @bot.event async def on_message(message): # Ignore messages from bots if message.author.bot: return # Only monitor specific channels if message.channel.id not in MONITORED_CHANNELS and message.channel.id != TEST_CHANNEL_ID: return # Check if message contains bug complaint if detect_bug_complaint(message.content): # Always respond in test channel, otherwise 10% chance if message.channel.id != TEST_CHANNEL_ID and random.random() > 0.05: return embed = discord.Embed( title="🐛 Bug Report Detected", description="It looks like you might be experiencing a bug! Please help us improve by submitting a detailed report.\n\n**Pro tip:** If you have exact steps to replicate the issue, that will help us fix it sooner! 🚀", color=0xff6b6b ) embed.add_field( name="Submit Feedback", value="[Click here to report the issue](https://suno.com/feedback)", inline=False ) embed.add_field( name="Need Help?", value="Use the button below to get assistance from a moderator who can help reproduce and verify the issue.", inline=False ) embed.set_thumbnail(url="attachment://sunoimg.png") embed.set_footer(text="Thank you for helping us make Suno better! 🎵") file = discord.File("sunoimg.png") view = BugReportView(message) bot_response = await message.reply(embed=embed, file=file, view=view, mention_author=True) view.bot_response = bot_response # Store reference to the bot's response # Send notification to Slack await send_slack_notification(message, view) # Process commands await bot.process_commands(message) if __name__ == '__main__': bot.run(os.getenv('DISCORD_TOKEN'))