hi
This commit is contained in:
415
scripts/roast-bot.py
Executable file
415
scripts/roast-bot.py
Executable file
@@ -0,0 +1,415 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
"""
|
||||
Code Roast Bot - AI-powered sarcastic code review with professional implementation
|
||||
This script provides humorous code feedback while demonstrating real code analysis concepts
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import re
|
||||
import json
|
||||
import random
|
||||
import argparse
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
from typing import Dict, List, Optional
|
||||
|
||||
class CodeRoastBot:
|
||||
"""Professional code analysis with humorous feedback delivery"""
|
||||
|
||||
def __init__(self, roast_intensity: int = 7):
|
||||
self.roast_intensity = min(10, max(1, roast_intensity))
|
||||
self.roast_history = []
|
||||
|
||||
# Professional code analysis patterns
|
||||
self.code_patterns = {
|
||||
'long_function': {'regex': r'def\s+\w+\([^)]*\):.*\n(?:\s+.*\n){20,}', 'severity': 'medium'},
|
||||
'deep_nesting': {'regex': r'(if|for|while|try).*\n(\s+){8}', 'severity': 'high'},
|
||||
'long_line': {'regex': r'.{100,}', 'severity': 'low'},
|
||||
'magic_numbers': {'regex': r'\b\d{2,}\b(?!\s*#.*magic)', 'severity': 'medium'},
|
||||
'todo_comments': {'regex': r'#\s*TODO|FIXME|HACK', 'severity': 'low'},
|
||||
'complex_regex': {'regex': r're\.compile\([^)]{100,}\)', 'severity': 'high'},
|
||||
'multiple_returns': {'regex': r'return.*\n.*return', 'severity': 'medium'},
|
||||
'bare_except': {'regex': r'except\s*:', 'severity': 'high'},
|
||||
'global_variables': {'regex': r'^\s*[A-Z_]+\s*=', 'severity': 'medium'},
|
||||
'long_imports': {'regex': r'from\s+\w+(\.\w+)*\s+import\s+[^,]+,', 'severity': 'low'}
|
||||
}
|
||||
|
||||
# Professional feedback templates
|
||||
self.professional_feedback = {
|
||||
'long_function': [
|
||||
"Consider breaking this function into smaller, more focused units",
|
||||
"This function might benefit from the Single Responsibility Principle",
|
||||
"Function complexity could be reduced by extracting helper methods"
|
||||
],
|
||||
'deep_nesting': [
|
||||
"Deep nesting can make code difficult to read and maintain",
|
||||
"Consider extracting nested logic into separate functions",
|
||||
"Guard clauses or early returns might simplify this structure"
|
||||
],
|
||||
'long_line': [
|
||||
"Line length exceeds typical style guide recommendations",
|
||||
"Consider breaking long lines for better readability",
|
||||
"PEP 8 suggests limiting lines to 79 characters"
|
||||
],
|
||||
'magic_numbers': [
|
||||
"Magic numbers should be replaced with named constants",
|
||||
"Consider defining these values as named constants for clarity",
|
||||
"Magic numbers reduce code maintainability"
|
||||
],
|
||||
'todo_comments': [
|
||||
"TODO comments should be addressed before production deployment",
|
||||
"Consider creating proper tickets for TODO items",
|
||||
"FIXME comments indicate technical debt that should be resolved"
|
||||
],
|
||||
'complex_regex': [
|
||||
"Complex regular expressions should be documented",
|
||||
"Consider breaking complex regex into smaller, named components",
|
||||
"Regex complexity makes maintenance difficult"
|
||||
],
|
||||
'multiple_returns': [
|
||||
"Multiple return points can make function flow harder to follow",
|
||||
"Consider restructuring to have a single exit point",
|
||||
"Multiple returns are acceptable but should be used judiciously"
|
||||
],
|
||||
'bare_except': [
|
||||
"Bare except clauses can hide important errors",
|
||||
"Specify the exceptions you want to catch",
|
||||
"Bare except makes debugging more difficult"
|
||||
],
|
||||
'global_variables': [
|
||||
"Global variables can make code harder to test and maintain",
|
||||
"Consider dependency injection instead of global state",
|
||||
"Global variables reduce code modularity"
|
||||
],
|
||||
'long_imports': [
|
||||
"Long import lines can be hard to read",
|
||||
"Consider using multiple import statements",
|
||||
"Import organization improves code readability"
|
||||
]
|
||||
}
|
||||
|
||||
# Humorous roast templates (professional but entertaining)
|
||||
self.roast_templates = {
|
||||
'low': [
|
||||
"This code is {issue}, but we've all been there. No judgment!",
|
||||
"Found {issue} - minor issue, but worth noting for future reference.",
|
||||
"Code has {issue}. Consider fixing it when you have a spare moment.",
|
||||
"Detecting {issue} - not the end of the world, but room for improvement."
|
||||
],
|
||||
'medium': [
|
||||
"Well hello there, {issue}! Your code decided to be 'creative' today.",
|
||||
"This code has {issue}. It's like the code equivalent of wearing socks with sandals.",
|
||||
"Found {issue} in your code. It's not broken, but it's definitely... interesting.",
|
||||
"Your code exhibits {issue}. The compiler is giving you the side-eye right now."
|
||||
],
|
||||
'high': [
|
||||
"WOW! This code has {issue}. That's... one way to solve the problem!",
|
||||
"I'm not angry, I'm just disappointed that I found {issue} in your code.",
|
||||
"This code has {issue}. It's so bold it makes me respect it, then fear it.",
|
||||
"Congratulations! Your code achieved {issue}. That's not something you see every day."
|
||||
]
|
||||
}
|
||||
|
||||
# Code quality assessments
|
||||
self.quality_assessments = [
|
||||
"This code has more issues than a comic book store.",
|
||||
"Your code is like a box of chocolates - full of surprises.",
|
||||
"This code writes itself - unfortunately, it didn't study programming first.",
|
||||
"Your code is so unique, it probably has its own programming paradigm.",
|
||||
"This code is breaking new ground - unfortunately, it's the ground of common sense.",
|
||||
"Your code is like modern art - some people get it, most people don't.",
|
||||
"This code has more personality than a sitcom character.",
|
||||
"Your code is the reason why 'code review' was invented.",
|
||||
"This code is so creative, it makes abstract art look straightforward.",
|
||||
"Your code is like a puzzle - the main puzzle is figuring out what it does."
|
||||
]
|
||||
|
||||
def analyze_file(self, file_path: str) -> Dict:
|
||||
"""Analyze a single file for code issues"""
|
||||
issues = []
|
||||
total_lines = 0
|
||||
|
||||
try:
|
||||
with open(file_path, 'r', encoding='utf-8') as f:
|
||||
content = f.read()
|
||||
lines = content.split('\n')
|
||||
total_lines = len(lines)
|
||||
|
||||
for pattern_name, pattern_info in self.code_patterns.items():
|
||||
matches = re.finditer(pattern_info['regex'], content, re.MULTILINE | re.DOTALL)
|
||||
for match in matches:
|
||||
# Find line number
|
||||
line_number = content[:match.start()].count('\n') + 1
|
||||
line_content = lines[line_number - 1] if line_number <= len(lines) else ""
|
||||
|
||||
issues.append({
|
||||
'type': pattern_name,
|
||||
'line': line_number,
|
||||
'content': line_content.strip(),
|
||||
'severity': pattern_info['severity'],
|
||||
'match_text': match.group()
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
issues.append({
|
||||
'type': 'file_error',
|
||||
'line': 0,
|
||||
'content': f"Could not read file: {str(e)}",
|
||||
'severity': 'high',
|
||||
'match_text': ''
|
||||
})
|
||||
|
||||
return {
|
||||
'file_path': file_path,
|
||||
'total_lines': total_lines,
|
||||
'issues': issues,
|
||||
'issue_count': len(issues)
|
||||
}
|
||||
|
||||
def generate_roast(self, analysis_result: Dict) -> str:
|
||||
"""Generate humorous but professional feedback"""
|
||||
issues = analysis_result['issues']
|
||||
file_path = analysis_result['file_path']
|
||||
|
||||
if not issues:
|
||||
return f"🎉 {file_path} is surprisingly clean! Either you're a coding genius or this file is empty. Either way, well done!"
|
||||
|
||||
# Generate professional summary
|
||||
summary_lines = []
|
||||
summary_lines.append(f"📋 Code Analysis for {file_path}")
|
||||
summary_lines.append(f" Total lines: {analysis_result['total_lines']}")
|
||||
summary_lines.append(f" Issues found: {len(issues)}")
|
||||
summary_lines.append("")
|
||||
|
||||
# Group issues by severity
|
||||
severity_counts = {'low': 0, 'medium': 0, 'high': 0}
|
||||
for issue in issues:
|
||||
if issue['severity'] in severity_counts:
|
||||
severity_counts[issue['severity']] += 1
|
||||
|
||||
summary_lines.append("📊 Issue Breakdown:")
|
||||
for severity, count in severity_counts.items():
|
||||
if count > 0:
|
||||
summary_lines.append(f" {severity.title()}: {count}")
|
||||
|
||||
summary_lines.append("")
|
||||
|
||||
# Add specific feedback for each issue
|
||||
summary_lines.append("🔍 Detailed Feedback:")
|
||||
for issue in issues[:5]: # Limit to top 5 issues for readability
|
||||
if issue['type'] in self.professional_feedback:
|
||||
professional = random.choice(self.professional_feedback[issue['type']])
|
||||
roast = self._generate_roast_for_issue(issue)
|
||||
|
||||
summary_lines.append(f"Line {issue['line']}: {issue['type']}")
|
||||
summary_lines.append(f" 💡 {professional}")
|
||||
if self.roast_intensity >= 6:
|
||||
summary_lines.append(f" 😄 {roast}")
|
||||
summary_lines.append("")
|
||||
|
||||
# Add overall assessment
|
||||
if len(issues) > 10:
|
||||
assessment = random.choice(self.quality_assessments)
|
||||
summary_lines.append(f"🎭 Overall Assessment: {assessment}")
|
||||
|
||||
# Add encouragement
|
||||
summary_lines.append("")
|
||||
summary_lines.append("💪 Remember: Every great developer was once a beginner. Keep coding, keep learning!")
|
||||
summary_lines.append("🚀 Professional tip: Use linters and formatters to catch these issues automatically.")
|
||||
|
||||
return "\n".join(summary_lines)
|
||||
|
||||
def _generate_roast_for_issue(self, issue: Dict) -> str:
|
||||
"""Generate a roast for a specific issue"""
|
||||
severity = issue['severity']
|
||||
issue_type = issue['type']
|
||||
|
||||
if severity not in self.roast_templates:
|
||||
severity = 'medium'
|
||||
|
||||
template = random.choice(self.roast_templates[severity])
|
||||
|
||||
# Convert issue type to readable format
|
||||
readable_issue = issue_type.replace('_', ' ').title()
|
||||
|
||||
return template.format(issue=readable_issue)
|
||||
|
||||
def analyze_directory(self, directory: str) -> Dict:
|
||||
"""Analyze all files in a directory"""
|
||||
results = {}
|
||||
total_issues = 0
|
||||
|
||||
# Supported file extensions
|
||||
extensions = ['.py', '.js', '.ts', '.java', '.cpp', '.c', '.go', '.rs', '.rb']
|
||||
|
||||
for root, dirs, files in os.walk(directory):
|
||||
# Skip common directories to ignore
|
||||
dirs[:] = [d for d in dirs if d not in ['.git', '__pycache__', 'node_modules', '.venv']]
|
||||
|
||||
for file in files:
|
||||
if any(file.endswith(ext) for ext in extensions):
|
||||
file_path = os.path.join(root, file)
|
||||
result = self.analyze_file(file_path)
|
||||
results[file_path] = result
|
||||
total_issues += result['issue_count']
|
||||
|
||||
return {
|
||||
'directory': directory,
|
||||
'files_analyzed': len(results),
|
||||
'total_issues': total_issues,
|
||||
'results': results
|
||||
}
|
||||
|
||||
def generate_directory_report(self, analysis: Dict) -> str:
|
||||
"""Generate a comprehensive report for directory analysis"""
|
||||
report_lines = []
|
||||
|
||||
report_lines.append("🎪 CI/CD Chaos Code Roast Report")
|
||||
report_lines.append("=" * 50)
|
||||
report_lines.append(f"📁 Directory: {analysis['directory']}")
|
||||
report_lines.append(f"📄 Files analyzed: {analysis['files_analyzed']}")
|
||||
report_lines.append(f"🐛 Total issues found: {analysis['total_issues']}")
|
||||
report_lines.append("")
|
||||
|
||||
# Top problematic files
|
||||
sorted_files = sorted(analysis['results'].items(),
|
||||
key=lambda x: x[1]['issue_count'], reverse=True)
|
||||
|
||||
report_lines.append("🔥 Most Problematic Files:")
|
||||
for file_path, result in sorted_files[:5]:
|
||||
if result['issue_count'] > 0:
|
||||
report_lines.append(f" {file_path}: {result['issue_count']} issues")
|
||||
|
||||
report_lines.append("")
|
||||
|
||||
# Individual file summaries
|
||||
report_lines.append("📋 Individual File Analysis:")
|
||||
for file_path, result in sorted_files:
|
||||
if result['issue_count'] > 0:
|
||||
report_lines.append("-" * 40)
|
||||
report_lines.append(f"File: {file_path}")
|
||||
report_lines.append(f"Issues: {result['issue_count']}")
|
||||
|
||||
# Show top issues
|
||||
issues_by_type = {}
|
||||
for issue in result['issues']:
|
||||
issue_type = issue['type']
|
||||
if issue_type not in issues_by_type:
|
||||
issues_by_type[issue_type] = []
|
||||
issues_by_type[issue_type].append(issue['line'])
|
||||
|
||||
for issue_type, lines in issues_by_type.items():
|
||||
report_lines.append(f" {issue_type}: lines {', '.join(map(str, lines[:3]))}")
|
||||
|
||||
report_lines.append("")
|
||||
|
||||
# Add humorous summary
|
||||
if analysis['total_issues'] > 50:
|
||||
report_lines.append("🎭 Professional Assessment:")
|
||||
report_lines.append("This codebase has more personality flaws than a reality TV star.")
|
||||
report_lines.append("But don't worry - even the best developers write imperfect code.")
|
||||
report_lines.append("The important thing is that you're seeking to improve!")
|
||||
elif analysis['total_issues'] > 20:
|
||||
report_lines.append("🎭 Professional Assessment:")
|
||||
report_lines.append("Your code is like a diamond in the rough - valuable but needs polishing.")
|
||||
report_lines.append("Keep up the good work and continue refining your craft!")
|
||||
else:
|
||||
report_lines.append("🎭 Professional Assessment:")
|
||||
report_lines.append("Your code is surprisingly clean! You must be using good practices.")
|
||||
report_lines.append("Maintain this quality and you'll be a coding superstar!")
|
||||
|
||||
report_lines.append("")
|
||||
report_lines.append("🚀 Remember: Code reviews and analysis are tools for growth, not criticism.")
|
||||
report_lines.append("Every issue found is an opportunity to become a better developer.")
|
||||
|
||||
return "\n".join(report_lines)
|
||||
|
||||
def roast_commit_message(self, commit_message: str) -> str:
|
||||
"""Roast a commit message"""
|
||||
roasts = []
|
||||
|
||||
# Check commit message length
|
||||
if len(commit_message) < 10:
|
||||
roasts.append("This commit message is shorter than a developer's attention span during a 9 AM meeting.")
|
||||
elif len(commit_message) > 72:
|
||||
roasts.append("This commit message is longer than the actual changes. Someone's being thorough!")
|
||||
|
||||
# Check for common commit message patterns
|
||||
if "fix" in commit_message.lower() and "bug" in commit_message.lower():
|
||||
roasts.append("Fixing a bug with a commit message that mentions 'fix' and 'bug' - how meta!")
|
||||
|
||||
if "update" in commit_message.lower() and "readme" in commit_message.lower():
|
||||
roasts.append("Updating the README because the code was too confusing to understand on its own.")
|
||||
|
||||
if "wip" in commit_message.lower():
|
||||
roasts.append("Work In Progress - or as I call it, 'I broke something and I'll fix it later'.")
|
||||
|
||||
if "lol" in commit_message.lower() or "haha" in commit_message.lower():
|
||||
roasts.append("This commit message contains laughter. Let's hope the code is funnier than the joke!")
|
||||
|
||||
# Check for imperative mood
|
||||
if not commit_message.split()[0].endswith('ed') and not commit_message.split()[0].endswith('s'):
|
||||
roasts.append("Your commit message isn't in imperative mood. The git police are coming!")
|
||||
|
||||
if not roasts:
|
||||
roasts.append("This is actually a pretty good commit message. I'm genuinely impressed!")
|
||||
roasts.append("Professional, clear, and concise. Are you sure you're a real developer?")
|
||||
|
||||
return random.choice(roasts)
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description='Code Roast Bot - Professional code analysis with humor')
|
||||
parser.add_argument('path', nargs='?', help='File or directory to analyze')
|
||||
parser.add_argument('--intensity', '-i', type=int, default=7,
|
||||
help='Roast intensity (1-10, default: 7)')
|
||||
parser.add_argument('--commit', '-c', help='Roast a commit message')
|
||||
parser.add_argument('--output', '-o', help='Output file for report')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
bot = CodeRoastBot(args.intensity)
|
||||
|
||||
if args.commit:
|
||||
# Roast commit message
|
||||
roast = bot.roast_commit_message(args.commit)
|
||||
print(f"🎪 Commit Message Roast:")
|
||||
print(f"Message: {args.commit}")
|
||||
print(f"Roast: {roast}")
|
||||
return
|
||||
|
||||
if not args.path:
|
||||
print("Please provide a file or directory path, or use --commit for commit message roasting")
|
||||
parser.print_help()
|
||||
return
|
||||
|
||||
path = args.path
|
||||
|
||||
if os.path.isfile(path):
|
||||
# Analyze single file
|
||||
result = bot.analyze_file(path)
|
||||
roast = bot.generate_roast(result)
|
||||
print(roast)
|
||||
|
||||
elif os.path.isdir(path):
|
||||
# Analyze directory
|
||||
analysis = bot.analyze_directory(path)
|
||||
report = bot.generate_directory_report(analysis)
|
||||
print(report)
|
||||
|
||||
else:
|
||||
print(f"Path not found: {path}")
|
||||
return
|
||||
|
||||
# Save to file if requested
|
||||
if args.output:
|
||||
with open(args.output, 'w', encoding='utf-8') as f:
|
||||
if os.path.isfile(path):
|
||||
f.write(bot.generate_roast(bot.analyze_file(path)))
|
||||
else:
|
||||
f.write(bot.generate_directory_report(bot.analyze_directory(path)))
|
||||
print(f"\n📄 Report saved to: {args.output}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
Reference in New Issue
Block a user