Tutorials & Guides
How to Build a Discord Bot that Auto-Drafts GitHub Issues from Raw Chat Bug Reports Using Claude 3.5 Sonnet
Tired of copy-pasting messy, unstructured Discord bug reports into GitHub? Build a Python bot that uses Claude 3.5 Sonnet to automatically turn chat chaos into perfectly formatted markdown issues.
Updated 10/5/2026
The Problem with Discord Bug Reports
If you run an open-source project, an indie game, or a small SaaS, your Discord server is likely where bugs go to liveβand where your sanity goes to die. Users don't write structured bug reports. They write stream-of-consciousness essays. You get a screenshot, a vague complaint about something "not working," and maybe, if you're lucky, a mention of their operating system.
Copying these chaotic chat logs, deciphering what actually went wrong, and manually formatting them into clean GitHub issues is a soul-crushing chore.
We can do better. By combining a simple Python Discord bot with the reasoning power of /platforms/claude, we can build an automated triage assistant. It listens for a specific emoji reaction on any message, parses the messy chat context, structures it into a pristine markdown GitHub issue, and drafts it for you.
Here is how to build it.
The Architecture
The workflow is straightforward: 1. A user reports a bug in Discord. 2. You (or a moderator) react to the message with a specific emoji (let's use π). 3. The bot fetches the target message and its immediate context (to capture follow-up replies). 4. We send this unstructured text to Claude 3.5 Sonnet, asking it to extract the steps to reproduce, expected vs. actual behaviour, and system details. 5. The bot uses the GitHub API to draft the issue.
Let's get our environment set up.
Step 1: Dependencies and Environment
You will need a few libraries. We are using discord.py for the Discord interface, anthropic to talk to Claude, and PyGithub to handle the GitHub integration.
`bash
pip install discord.py anthropic PyGithub python-dotenv
`
Create a .env file in your project directory to store your credentials:
`env
DISCORD_TOKEN=your_discord_bot_token
ANTHROPIC_API_KEY=your_anthropic_api_key
GITHUB_TOKEN=your_github_personal_access_token
GITHUB_REPO=your_username/your_repo_name
`
(Note: If you run into issues authenticating your Anthropic client, check the official help docs at [https://claude-support.com](https://claude-support.com) for troubleshooting steps.)
Step 2: Designing the Claude Prompt
To ensure Claude returns a highly structured, reliable markdown payload, we need to instruct it clearly. We don't want conversational filler or polite banter. We want a clean GitHub-ready markdown document.
We will define a system prompt that enforces this structure. If you need help refining your LLM instructions for similar workflows, check out our interactive /prompts for inspiration.
Here is our system prompt:
`python
SYSTEM_PROMPT = """
You are an expert QA engineer and technical writer. Your task is to take a messy, unstructured chat log describing a software bug and convert it into a highly professional, structured GitHub Issue in markdown format.
Your output must contain only the markdown for the issue. Do not include any conversational intro or outro text.
Structure the issue as follows:
- Title: A concise, descriptive title (maximum 10 words).
- Description: A brief summary of the issue.
- Steps to Reproduce: A numbered list of steps, inferred logically from the chat text.
- Expected Behaviour: What should have happened.
- Actual Behaviour: What actually happened.
- Environment Info: Operating system, browser, or versions mentioned (or 'Not specified' if missing).
- Context: A quote of the original chat log for reference.
"""
`
Step 3: Writing the Bot Code
Now, let's write the core Python script. We'll set up a Discord client that listens for a raw_reaction_add event. This ensures the bot can process reactions even on older messages that aren't cached in memory.
`python
import os
import discord
from discord.ext import commands
from anthropic import Anthropic
from github import Github
from dotenv import load_dotenv
load_dotenv()
Initialize APIs discord_token = os.getenv("DISCORD_TOKEN") anthropic_client = Anthropic(api_key=os.getenv("ANTHROPIC_API_KEY")) github_client = Github(os.getenv("GITHUB_TOKEN")) repo = github_client.get_repo(os.getenv("GITHUB_REPO"))
Setup Discord Bot with necessary intents intents = discord.Intents.default() intents.message_content = True intents.reactions = True bot = commands.Bot(command_prefix="!", intents=intents)
@bot.event async def on_ready(): print(f"Logged in as {bot.user.name} (ID: {bot.user.id})") print("------")
@bot.event async def on_raw_reaction_add(payload): # We only care about the bug emoji if str(payload.emoji) != "π": return
channel = bot.get_channel(payload.channel_id) message = await channel.fetch_message(payload.message_id)
Optional: Restrict usage to moderators/admins # member = payload.member # if not member.guild_permissions.manage_messages: # return
Let the channel know we are on it await channel.send(f"βοΈ Processing bug report from {message.author.mention}...", delete_after=10)
Gather context: target message + up to 3 subsequent messages to catch details context_msgs = [] async for msg in channel.history(limit=5, after=message.created_at, oldest_first=True): context_msgs.append(f"{msg.author.display_name}: {msg.content}") chat_log = f"{message.author.display_name}: {message.content}\n" + "\n".join(context_msgs)
Send context to Claude try: prompt = f"Extract the bug report from this chat log:\n\n{chat_log}" response = anthropic_client.messages.create( model="claude-3-5-sonnet-20241022", max_tokens=1500, temperature=0.1, system=SYSTEM_PROMPT, messages=[{"role": "user", "content": prompt}] ) issue_markdown = response.content[0].text # Extract the first line as the title, clean up markdown headers lines = issue_markdown.strip().split("\n") title = "[Discord Bug] " + lines[0].replace("**Title**:", "").replace("#", "").strip() body = "\n".join(lines[1:]).strip()
Create the issue on GitHub created_issue = repo.create_issue(title=title, body=body)
Send a success message back to Discord await channel.send( f"β **GitHub Issue Created!**\n" f"Title: *{title}*\n" f"Link: {created_issue.html_url}" )
except Exception as e: await channel.send(f"β Failed to generate issue. Error: {str(e)}")
if __name__ == "__main__":
bot.run(discord_token)
`
Step 4: Testing Your Bot
Run your script localy to test things out:
`bash
python bot.py
`
Now, head over to your Discord server, post a messy report, and react to it with the π emoji.
For instance, if someone types:
> "Ugh, the login page keeps spinning when I use Safari on my phone. Works fine on my laptop but mobile is completely broken. I just get a grey screen and no error message."
Your bot will catch this, feed it to Claude, and create a perfectly structured issue on GitHub complete with "Steps to Reproduce" (1. Open mobile Safari, 2. Navigate to login page, etc.) and "Environment Info" (Safari on mobile), all without you lifting a finger.
Optimising the Pipeline
This simple setup is highly customisable. If your repository uses strict issue templates, you can easily modify the SYSTEM_PROMPT to output specific template keys or inject tags into your PyGithub call (e.g., tagging the issue as triage or discord-reported).
If you want to dive deeper into the mechanics of API payloads, schemas, and prompting architectures, take a look at our comprehensive /glossary for a breakdown of foundational LLM design patterns. It's a great way to keep your development pipeline ticking along nicely without manual intervention.
Keep going
Build something with the prompt generator, decode the jargon in the glossary, or compare the tools on our platform deep-dives.