1
+ import hmac
2
+ import hashlib
3
+ import logging
4
+ import requests
5
+ from pr_manager import PRManager
6
+
7
+ class GitHubBot :
8
+ def __init__ (self , token , webhook_secret ):
9
+ self .token = token
10
+ self .webhook_secret = webhook_secret .encode ()
11
+ self .pr_manager = PRManager ()
12
+ self .logger = logging .getLogger (__name__ )
13
+ self .headers = {
14
+ 'Authorization' : f'token { token } ' ,
15
+ 'Accept' : 'application/vnd.github.v3+json'
16
+ }
17
+
18
+ def verify_webhook (self , signature , payload ):
19
+ """Verify webhook signature."""
20
+ if not signature :
21
+ return False
22
+
23
+ expected = hmac .new (
24
+ self .webhook_secret ,
25
+ payload ,
26
+ hashlib .sha256
27
+ ).hexdigest ()
28
+ return hmac .compare_digest (f"sha256={ expected } " , signature )
29
+
30
+ def handle_pr_event (self , data ):
31
+ """Handle pull request events."""
32
+ action = data .get ('action' )
33
+ pr = data .get ('pull_request' )
34
+
35
+ if not pr :
36
+ return
37
+
38
+ if action == 'opened' :
39
+ self ._handle_new_pr (pr )
40
+ elif action == 'closed' :
41
+ self .pr_manager .remove_pr (pr ['number' ])
42
+
43
+ def _handle_new_pr (self , pr ):
44
+ """Handle new pull request."""
45
+ repo_url = pr ['base' ]['repo' ]['url' ]
46
+ pr_number = pr ['number' ]
47
+
48
+ comment = (
49
+ "👋 Hi! Would you like to pick specific reviewers for this PR? "
50
+ "If yes, please mention them in a comment. "
51
+ "If not, I'll automatically assign reviewers for you. "
52
+ "Please respond within 24 hours."
53
+ )
54
+
55
+ self ._create_comment (repo_url , pr_number , comment )
56
+ self .pr_manager .add_pr (pr_number , pr )
57
+
58
+ def handle_review_event (self , data ):
59
+ """Handle pull request review events."""
60
+ review = data .get ('review' )
61
+ pr = data .get ('pull_request' )
62
+
63
+ if not review or not pr :
64
+ return
65
+
66
+ if review ['state' ] == 'approved' :
67
+ self ._handle_approved_review (pr )
68
+ elif review ['state' ] == 'changes_requested' :
69
+ self ._handle_changes_requested (pr )
70
+
71
+ def _handle_approved_review (self , pr ):
72
+ """Handle approved review."""
73
+ comment = (
74
+ "✅ This PR has been approved! "
75
+ "Would you like another round of review? "
76
+ "Please let me know in a comment."
77
+ )
78
+ self ._create_comment (pr ['base' ]['repo' ]['url' ], pr ['number' ], comment )
79
+
80
+ def _handle_changes_requested (self , pr ):
81
+ """Handle changes requested review."""
82
+ comment = (
83
+ "📝 Changes have been requested. "
84
+ "Please address the feedback and let me know when you're ready for another review."
85
+ )
86
+ self ._create_comment (pr ['base' ]['repo' ]['url' ], pr ['number' ], comment )
87
+
88
+ def _create_comment (self , repo_url , pr_number , body ):
89
+ """Create a comment on a PR."""
90
+ comments_url = f"{ repo_url } /issues/{ pr_number } /comments"
91
+ response = requests .post (
92
+ comments_url ,
93
+ headers = self .headers ,
94
+ json = {'body' : body }
95
+ )
96
+ if response .status_code != 201 :
97
+ self .logger .error (f"Failed to create comment: { response .text } " )
98
+
99
+ def get_stats (self ):
100
+ """Get bot statistics."""
101
+ return {
102
+ 'active_prs' : len (self .pr_manager .prs ),
103
+ 'total_reviews' : self .pr_manager .total_reviews
104
+ }
0 commit comments