#!/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()