- Install python-telegram-bot dependency - Start Telegram bot service successfully - Confirm correct group ID (MOMO PRO - small shrimp group) - Bot now running with all commands and button interface functional - Natural language processing restored with keyword matching Fixes issue where Telegram group could not communicate using natural language.
301 lines
11 KiB
Python
301 lines
11 KiB
Python
#!/usr/bin/env python3
|
||
# ================= MOMO 系統 - Aider Code Review 自動化腳本 =================
|
||
# 功能:程式完成後自動觸發 Aider 進行 Code Review
|
||
# 作者:AI Assistant
|
||
# 版本:1.0
|
||
# =======================================================================
|
||
|
||
import argparse
|
||
import os
|
||
import subprocess
|
||
import sys
|
||
from datetime import datetime
|
||
from pathlib import Path
|
||
|
||
# 導入工具類
|
||
try:
|
||
from scripts.code_review_utils import CodeReviewLogger, CodeReviewConfig, CodeReviewValidator, GitHelper
|
||
except ImportError:
|
||
# 嘗試直接導入(當在scripts目錄中執行時)
|
||
try:
|
||
from code_review_utils import CodeReviewLogger, CodeReviewConfig, CodeReviewValidator, GitHelper
|
||
except ImportError:
|
||
print("❌ 無法導入工具類,請確保 code_review_utils.py 存在")
|
||
sys.exit(1)
|
||
|
||
|
||
class AiderCodeReview:
|
||
def __init__(self, project_root=None):
|
||
self.project_root = Path(project_root) if project_root else Path(__file__).parent.parent
|
||
|
||
# 初始化工具類
|
||
self.logger = CodeReviewLogger(self.project_root)
|
||
self.config = CodeReviewConfig(self.project_root)
|
||
self.validator = CodeReviewValidator()
|
||
self.git_helper = GitHelper(self.project_root)
|
||
|
||
# 檢查Git倉庫
|
||
if not self.git_helper.is_git_repository():
|
||
self.logger.warning("當前目錄不是Git倉庫,某些功能可能無法使用")
|
||
|
||
# 檢查Aider路徑
|
||
self.aider_path = self.config.get("aider.path", "/Users/ooo/.local/bin/aider")
|
||
if not Path(self.aider_path).exists():
|
||
self.logger.error(f"Aider 路徑不存在: {self.aider_path}")
|
||
|
||
def log_review(self, message):
|
||
"""記錄Code Review日誌(保持向後兼容)"""
|
||
self.logger.info(message)
|
||
|
||
def get_changed_files(self, git_diff_filter="ACM"):
|
||
"""獲取Git中變更的檔案"""
|
||
try:
|
||
# 使用GitHelper獲取變更檔案
|
||
all_changed = self.git_helper.get_changed_files(
|
||
diff_filter=git_diff_filter,
|
||
use_staged=self.config.get("git.use_staged", True)
|
||
)
|
||
|
||
# 過滤允許的檔案類型
|
||
allowed_extensions = self.config.get("file_filters.allowed_extensions",
|
||
['.py', '.js', '.ts', '.jsx', '.tsx', '.html', '.css'])
|
||
|
||
filtered_files = []
|
||
for file_path in all_changed:
|
||
if any(file_path.endswith(ext) for ext in allowed_extensions):
|
||
# 檢查檔案是否存在且大小合理
|
||
abs_path = self.project_root / file_path
|
||
if abs_path.exists() and abs_path.is_file():
|
||
file_size = abs_path.stat().st_size
|
||
max_size = self.config.get("file_filters.max_file_size", 10485760)
|
||
if file_size <= max_size:
|
||
filtered_files.append(file_path)
|
||
else:
|
||
self.logger.warning(f"檔案過大,跳過: {file_path} ({file_size} bytes)")
|
||
else:
|
||
self.logger.warning(f"檔案不存在,跳過: {file_path}")
|
||
|
||
return filtered_files
|
||
|
||
except Exception as e:
|
||
self.logger.error("獲取變更檔案失敗", e)
|
||
return []
|
||
|
||
def run_aider_review(self, files=None, review_type="basic"):
|
||
"""執行Aider Code Review"""
|
||
if not files:
|
||
files = self.get_changed_files()
|
||
|
||
if not files:
|
||
self.log_review("📝 沒有需要Review的檔案")
|
||
return True
|
||
|
||
# 驗證Review類型
|
||
if not self.validator.validate_review_type(review_type):
|
||
self.logger.error(f"無效的Review類型: {review_type}")
|
||
return False
|
||
|
||
# 驗證檔案
|
||
valid_files = self.validator.validate_files(files, self.project_root)
|
||
if not valid_files:
|
||
self.logger.error("沒有有效的檔案可以Review")
|
||
return False
|
||
|
||
# 構建Aider命令
|
||
cmd = [self.aider_path]
|
||
|
||
# 從配置獲取Review類型對應的訊息
|
||
review_message = self.config.get(f"review_types.{review_type}.message")
|
||
if not review_message:
|
||
self.logger.error(f"找不到Review類型 {review_type} 的配置")
|
||
return False
|
||
|
||
# 添加Aider參數
|
||
if self.config.get("aider.auto_yes", False):
|
||
cmd.append("--yes")
|
||
|
||
if self.config.get("aider.no_git", True):
|
||
cmd.append("--no-git")
|
||
|
||
cmd.extend(["--message", review_message])
|
||
|
||
# 添加要review的檔案
|
||
cmd.extend(valid_files)
|
||
|
||
self.log_review(f"🔍 開始Code Review - 類型: {review_type}, 檔案: {', '.join(valid_files)}")
|
||
|
||
try:
|
||
# 設置環境變數
|
||
env = os.environ.copy()
|
||
env["PYTHONPATH"] = str(self.project_root)
|
||
|
||
# 執行Aider
|
||
timeout = self.config.get("aider.timeout", 600)
|
||
result = subprocess.run(
|
||
cmd,
|
||
cwd=self.project_root,
|
||
capture_output=True,
|
||
text=True,
|
||
env=env,
|
||
timeout=timeout
|
||
)
|
||
|
||
if result.returncode == 0:
|
||
self.log_review("✅ Code Review 完成")
|
||
# 保存Review結果
|
||
self.save_review_result(valid_files, result.stdout, review_type)
|
||
return True
|
||
else:
|
||
self.logger.error(f"Code Review 失敗: {result.stderr}")
|
||
return False
|
||
|
||
except subprocess.TimeoutExpired:
|
||
self.logger.error("Code Review 超時")
|
||
return False
|
||
except Exception as e:
|
||
self.logger.error("Code Review 異常", e)
|
||
return False
|
||
|
||
def save_review_result(self, files, output, review_type):
|
||
"""保存Review結果到檔案"""
|
||
try:
|
||
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
||
safe_review_type = self.validator.sanitize_filename(review_type)
|
||
result_file = self.project_root / "logs" / f"review_{safe_review_type}_{timestamp}.md"
|
||
|
||
content = f"""# Code Review 報告
|
||
|
||
**時間**: {datetime.now().strftime("%Y-%m-%d %H:%M:%S")}
|
||
**類型**: {review_type}
|
||
**檔案**: {', '.join(files)}
|
||
**Aider版本**: {self.aider_path}
|
||
|
||
## Review 結果
|
||
|
||
```
|
||
{output}
|
||
```
|
||
|
||
## 執行資訊
|
||
|
||
- **專案根目錄**: {self.project_root}
|
||
- **檔案數量**: {len(files)}
|
||
- **Review類型**: {review_type}
|
||
- **執行時間**: {datetime.now().strftime("%Y-%m-%d %H:%M:%S")}
|
||
|
||
---
|
||
*由 Aider 自動生成 - MOMO 系統 Code Review*
|
||
"""
|
||
|
||
with open(result_file, "w", encoding="utf-8") as f:
|
||
f.write(content)
|
||
|
||
self.log_review(f"📄 Review結果已保存: {result_file}")
|
||
|
||
except Exception as e:
|
||
self.logger.error("保存Review結果失敗", e)
|
||
|
||
def review_specific_files(self, file_paths, review_type="basic"):
|
||
"""Review指定的檔案"""
|
||
try:
|
||
# 驗證Review類型
|
||
if not self.validator.validate_review_type(review_type):
|
||
self.logger.error(f"無效的Review類型: {review_type}")
|
||
return False
|
||
|
||
# 驗證並處理檔案路徑
|
||
valid_files = self.validator.validate_files(file_paths, self.project_root)
|
||
if not valid_files:
|
||
self.logger.error("沒有有效的檔案可以Review")
|
||
return False
|
||
|
||
return self.run_aider_review(valid_files, review_type)
|
||
|
||
except Exception as e:
|
||
self.logger.error("Review指定檔案失敗", e)
|
||
return False
|
||
|
||
|
||
def main():
|
||
try:
|
||
parser = argparse.ArgumentParser(
|
||
description="MOMO系統 Aider Code Review 自動化工具",
|
||
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||
epilog="""
|
||
使用範例:
|
||
%(prog)s --auto # 自動Review暫存檔案
|
||
%(prog)s --files app.py routes/*.py # Review指定檔案
|
||
%(prog)s --type security --auto # 安全檢查暫存檔案
|
||
%(prog)s --project-root /path/to/project # 指定專案根目錄
|
||
"""
|
||
)
|
||
parser.add_argument("--files", "-f", nargs="+",
|
||
help="指定要Review的檔案(支援相對路徑和絕對路徑)")
|
||
parser.add_argument("--type", "-t", choices=["basic", "security", "performance"],
|
||
default="basic", help="Review類型 (預設: basic)")
|
||
parser.add_argument("--project-root", "-p",
|
||
help="專案根目錄路徑(預設: 自動偵測)")
|
||
parser.add_argument("--auto", "-a", action="store_true",
|
||
help="自動Review暫存的檔案 (Git staged files)")
|
||
parser.add_argument("--verbose", "-v", action="store_true",
|
||
help="顯示詳細輸出")
|
||
parser.add_argument("--dry-run", action="store_true",
|
||
help="僅顯示將要Review的檔案,不實際執行")
|
||
|
||
args = parser.parse_args()
|
||
|
||
# 初始化Code Review工具
|
||
reviewer = AiderCodeReview(args.project_root)
|
||
|
||
if args.verbose:
|
||
reviewer.logger.info(f"🔧 使用專案根目錄: {reviewer.project_root}")
|
||
reviewer.logger.info(f"🔧 Aider 路徑: {reviewer.aider_path}")
|
||
|
||
success = False
|
||
|
||
if args.dry_run:
|
||
# 乾運行模式
|
||
if args.auto:
|
||
files = reviewer.get_changed_files()
|
||
reviewer.logger.info(f"🔍 將Review的暫存檔案: {', '.join(files)}")
|
||
elif args.files:
|
||
valid_files = reviewer.validator.validate_files(args.files, reviewer.project_root)
|
||
reviewer.logger.info(f"🔍 將Review的指定檔案: {', '.join(valid_files)}")
|
||
else:
|
||
files = reviewer.get_changed_files()
|
||
reviewer.logger.info(f"🔍 將Review的變更檔案: {', '.join(files)}")
|
||
success = True
|
||
elif args.auto:
|
||
# 自動Review暫存的檔案
|
||
reviewer.logger.info("🚀 開始自動Review暫存檔案...")
|
||
success = reviewer.run_aider_review(review_type=args.type)
|
||
elif args.files:
|
||
# Review指定檔案
|
||
reviewer.logger.info(f"🚀 開始Review指定檔案: {', '.join(args.files)}")
|
||
success = reviewer.review_specific_files(args.files, args.type)
|
||
else:
|
||
# Review所有變更檔案
|
||
reviewer.logger.info("🚀 開始Review所有變更檔案...")
|
||
success = reviewer.run_aider_review(review_type=args.type)
|
||
|
||
if success:
|
||
reviewer.logger.info("✅ Code Review 流程完成")
|
||
sys.exit(0)
|
||
else:
|
||
reviewer.logger.error("❌ Code Review 流程失敗")
|
||
sys.exit(1)
|
||
|
||
except KeyboardInterrupt:
|
||
print("\n⚠️ 用戶中斷操作")
|
||
sys.exit(130)
|
||
except Exception as e:
|
||
print(f"❌ 程式執行錯誤: {e}")
|
||
if "--verbose" in sys.argv or "-v" in sys.argv:
|
||
import traceback
|
||
traceback.print_exc()
|
||
sys.exit(1)
|
||
|
||
|
||
if __name__ == "__main__":
|
||
main()
|