diff --git a/tools/github_webhook_processor.php b/tools/github_webhook_processor.php index 4ccdafa3f38..c43c7edf7eb 100644 --- a/tools/github_webhook_processor.php +++ b/tools/github_webhook_processor.php @@ -26,6 +26,17 @@ $hookSecret = '08ajh0qj93209qj90jfq932j32r'; //Api key for pushing changelogs. $apiKey = '209ab8d879c0f987d06a09b9d879c0f987d06a09b9d8787d0a089c'; +//anti-spam measures. Don't announce PRs in game to people unless they've gotten a pr merged before +//options are: +// "repo" - user has to have a pr merged in the repo before. +// "org" - user has to have a pr merged in any repo in the organization (for repos owned directly by users, this applies to any repo directly owned by the same user.) +// "disable" - disables. +//defaults to org if left blank or given invalid values. +$validation = "org"; + +//how many merged prs must they have under the rules above to have their pr announced to the game servers. +$validation_count = 1; + //servers to announce PRs to. $servers = array(); /* @@ -39,6 +50,7 @@ $servers[1]['port'] = '2337'; $servers[1]['comskey'] = '89aj90cq2fm0amc90832mn9rm90'; */ + //CONFIG END set_error_handler(function($severity, $message, $file, $line) { throw new \ErrorException($message, 0, $severity, $file, $line); @@ -106,27 +118,57 @@ switch (strtolower($_SERVER['HTTP_X_GITHUB_EVENT'])) { die(); } -//rip bs-12 -function tag_pr($payload, $opened) { +function apisend($url, $method = 'GET', $content = NULL) { global $apiKey; - - //We need to reget the pull_request part of the payload to actually see the mergeable field populated - //http://stackoverflow.com/questions/30619549/why-does-github-api-return-an-unknown-mergeable-state-in-a-pull-request + if (is_array($content)) + $content = json_encode($content); + $scontext = array('http' => array( - 'method' => 'GET', + 'method' => $method, 'header' => "Content-type: application/json\r\n". 'Authorization: token ' . $apiKey, 'ignore_errors' => true, 'user_agent' => 'tgstation13.org-Github-Automation-Tools' )); - - $url = $payload['pull_request']['url']; - $payload['pull_request'] = json_decode(file_get_contents($url, false, stream_context_create($scontext)), true); + if ($content) + $scontext['content'] = $content; + + return file_get_contents($url, false, stream_context_create($scontext)); +} +function validate_user($payload) { + global $validation, $validation_count; + $query = array(); + if (empty($validation)) + $validation = 'org'; + switch (strtolower($validation)) { + case 'disable': + return TRUE; + case 'repo': + $query['repo'] = $payload['pull_request']['base']['repo']['full_name']; + break; + default: + $query['user'] = $payload['pull_request']['base']['repo']['owner']['login']; + break; + } + $query['author'] = $payload['pull_request']['user']['login']; + $query['is'] = 'merged'; + $querystring = ''; + foreach($query as $key => $value) + $querystring .= ($querystring == '' ? '' : '+') . urlencode($key) . ':' . urlencode($value); + $res = apisend('https://api.github.com/search/issues?q='.$querystring); + $res = json_decode($res, TRUE); + return $res['total_count'] >= (int)$validation_count; + +} +//rip bs-12 +function tag_pr($payload, $opened) { + //get the mergeable state + $payload['pull_request'] = json_decode(apisend($url)); if($payload['pull_request']['mergeable'] == null) { //STILL not ready. Give it a bit, then try one more time sleep(10); - $payload['pull_request'] = json_decode(file_get_contents($url, false, stream_context_create($scontext)), true); + $payload['pull_request'] = json_decode(apisend($url)); } $tags = array(); @@ -184,14 +226,13 @@ function tag_pr($payload, $opened) { foreach($tags as $t) $final[] = $t; - $scontext['http']['method'] = 'PUT'; - $scontext['http']['content'] = json_encode($final); - echo file_get_contents($url, false, stream_context_create($scontext)); + echo apisend($url, 'PUT', $final); } function handle_pr($payload) { $action = 'opened'; + $validated = validate_user($payload); switch ($payload["action"]) { case 'opened': tag_pr($payload, true); @@ -210,6 +251,7 @@ function handle_pr($payload) { else { $action = 'merged'; checkchangelog($payload, true, true); + $validated = TRUE; //pr merged events always get announced. } break; default: @@ -220,7 +262,11 @@ function handle_pr($payload) { echo "PR Announcement Halted; Secret tag detected.\n"; return; } - + if (!$validated) { + echo "PR Announcement Halted; User not validated.\n"; + return; + } + $msg = '['.$payload['pull_request']['base']['repo']['full_name'].'] Pull Request '.$action.' by '.htmlSpecialChars($payload['sender']['login']).': '.htmlSpecialChars('#'.$payload['pull_request']['number'].' '.$payload['pull_request']['user']['login'].' - '.$payload['pull_request']['title']).''; sendtoallservers('?announce='.urlencode($msg), $payload); @@ -236,7 +282,6 @@ function has_tree_been_edited($payload, $tree){ } function checkchangelog($payload, $merge = false, $compile = true) { - global $apiKey; if (!$merge) return; if (!isset($payload['pull_request']) || !isset($payload['pull_request']['body'])) { @@ -413,17 +458,9 @@ function checkchangelog($payload, $merge = false, $compile = true) { 'message' => 'Automatic changelog generation for PR #'.$payload['pull_request']['number'].' [ci skip]', 'content' => base64_encode($file) ); - $scontext = array('http' => array( - 'method' => 'PUT', - 'header' => - "Content-type: application/json\r\n". - 'Authorization: token ' . $apiKey, - 'content' => json_encode($content), - 'ignore_errors' => true, - 'user_agent' => 'tgstation13.org-Github-Automation-Tools' - )); + $filename = '/html/changelogs/AutoChangeLog-pr-'.$payload['pull_request']['number'].'.yml'; - echo file_get_contents($payload['pull_request']['base']['repo']['url'].'/contents'.$filename, false, stream_context_create($scontext)); + echo apisend($payload['pull_request']['base']['repo']['url'].'/contents'.$filename, 'PUT', $context); } function sendtoallservers($str, $payload = null) {