{"action":"created","comment":{"url":"https://api.github.com/repos/numenta/nupic.tools/pulls/comments/34208094","id":34208094,"diff_hunk":"@@ -14,108 +52,220 @@ var CronJob = require('cron').CronJob\n   , prReviewerEmail\n   ;\n \n-function sendPrReviewReminder(prs) {\n-    var to = prReviewerEmail\n-      , subject = prs.length + ' NuPIC Pull Requests need review'\n-      , body = ''\n+\n+/**\n+ * Got the Pull Requests, start processing on them now\n+ * @function\n+ * @param {Array} prs - List of Pull Request objects from GitHub API\n+ * @private\n+ */\n+var processAllOpenPrs = function (prs) {\n+  var warn = []\n+    , close = []\n+    , email = []\n+    ;\n+\n+  log.info('Found %s open pull requests.', prs.length);\n+\n+  // queue actions\n+  _.each(prs, function(pr) {\n+    var labels = _.pluck(pr.labels, 'name')\n+      , updated = moment(pr.updated_at).subtract(26, 'days')\n+      , sevenDaysAgo = moment().subtract(7, 'days')\n+      , almostMonthAgo= moment().subtract(25, 'days')\n+      , monthAgo = moment().subtract(1, 'month')\n       ;\n \n-    if (! to) {\n-        log.error('No one to email PR review emails to!');\n-        return;\n+    if (_.contains(labels, readyLabel)) {\n+      // This PR is \"ready\".\n+      if (moment(updated).isBefore(sevenDaysAgo)) {\n+        email.push(pr);\n+      }\n     }\n+    else if (\n+      _.contains(labels, inProgressLabel) ||\n+      _.contains(labels, helpWantedLabel)\n+    ) {\n+      if (moment(updated).isBefore(monthAgo)) {\n+        // This PR is expired and \"closing\"\n+        close.push(pr);\n+      }\n+      else if (moment(updated).isBefore(almostMonthAgo)) {\n+        // This PR is getting warned about expiring soon\n+        warn.push(pr);\n+      }\n+    }\n+  });\n \n-    body += 'Hello NuPIC Committers! Here is a list of pull requests awaiting\\n'\n-          + 'review:\\n\\n';\n+  // execute queued actions\n+  if (email.length) {\n+    sendPrReviewReminder(email);\n+  }\n+  if (close.length) {\n+    closePrExpired(close);\n+  }\n+  if (warn.length) {\n+    warnPrExpiring(warn);\n+  }\n+};\n \n-    _.each(prs, function(pr) {\n-        body += '- ' + pr.title + ' --- ' + pr.html_url + '\\n';\n-    });\n+/**\n+ * Add a comment to an old PR telling everyone it will be expired soon\n+ * @function\n+ * @param {Array} prs - List of Pull Request objects from GitHub API\n+ * @private\n+ */\n+var sendPrReviewReminder = function (prs) {\n+  var to = prReviewerEmail\n+    , subject = prs.length + ' NuPIC Pull Requests need review'\n+    , body = ''\n+    ;\n \n-    body += '\\nThese pull requests have been ready for review for over a\\n'\n-          + 'week! Please make it a priority to review these contributions\\n'\n-          + 'or discuss reasons why they cannot be merged.\\n\\n';\n-\n-    sendMail(to, subject, body, function(error) {\n-        if (error) {\n-            log.error('Error running cron job ' +\n-                '\"Pull Request Reviewer\" (sending mail).');\n-            log.error(error);\n-        } else {\n-            log.debug('Mail sent successfully.');\n-        }\n-    });\n-}\n+  log.info('Sending Review Reminders for %s old open pull requests.', prs.length);\n \n-function processAllOpenPrs(prs) {\n-    var warn = []\n-      , close = []\n-      , email = []\n-      ;\n-    log.info('Found %s open pull requests.', prs.length);\n-    _.each(prs, function(pr) {\n-        var labels = _.pluck(pr.labels, 'name')\n-          , updated = new Date(pr.updated_at)\n-          , sevenDaysAgo = moment().subtract(7, 'days')\n-          , almostMonthAgo= moment().subtract(25, 'days')\n-          , monthAgo = moment().subtract(1, 'month')\n-          ;\n-        if (_.contains(labels, readyLabel)) {\n-            // This PR is \"ready\".\n-            if (moment(updated).isBefore(sevenDaysAgo)) {\n-                email.push(pr);\n-            }\n-        } else if (_.contains(labels, inProgressLabel)\n-                || _.contains(labels, helpWantedLabel)) {\n-            if (moment(updated).isBefore(monthAgo)) {\n-                close.push(pr);\n-            } else if (moment(updated).isBefore(almostMonthAgo)) {\n-                warn.push(pr);\n-            }\n-        }\n-    });\n-    if (email.length) {\n-        sendPrReviewReminder(email);\n+  if (! to) {\n+    log.error('No one to email PR review emails to!');\n+    return;\n+  }\n+\n+  body += 'Hello NuPIC Committers! Here is a list of pull requests awaiting\\n'\n+        + 'review:\\n\\n';\n+\n+  _.each(prs, function(pr) {\n+    body += '- ' + pr.title + ' --- ' + pr.html_url + '\\n';\n+  });\n+\n+  body += '\\nThese pull requests have been ready for review for over a\\n'\n+        + 'week! Please make it a priority to review these contributions\\n'\n+        + 'or discuss reasons why they cannot be merged.\\n\\n';\n+\n+  sendMail(to, subject, body, function(error) {\n+    if (error) {\n+      log.error(\n+        'Error running cron job ' + '\"Pull Request Reviewer\" (sending mail).'\n+      );\n+      log.error(error);\n+    } else {\n+      log.debug('Mail sent successfully.');\n     }\n-}\n-\n-\n-function reviewPullRequests(config, repoClients) {\n-    var job;\n-    prReviewerEmail = config.notifications.pr_review;\n-    job = new CronJob('5 0 * * *', function() {\n-        var prFetchers = []\n-          , prs = [];\n-        log.info('Starting open PR review...');\n-        _.each(repos, function(repo) {\n-            var repoClient = repoClients[repo];\n-            prFetchers.push(function(callback) {\n-                repoClient.getAllOpenPullRequests(\n-                    {includeLabels: true}, callback\n-                );\n-            });\n-        });\n+  });\n+};\n \n-        async.parallel(prFetchers, function(error, prLists) {\n-            if (error) {\n-                log.error('Error running cron job \"%s\"!', job.name);\n-                log.error(error);\n-            } else {\n-                _.each(prLists, function(prList) {\n-                    prs = prs.concat(prList);\n-                });\n-                processAllOpenPrs(prs);\n-            }\n+/**\n+ * Close old expired Pull Requests (older than a month)\n+ * @function\n+ * @param {Array} prs - List of Pull Request objects from GitHub API\n+ * @private\n+ */\n+var closePrExpired = function (prs) {\n+  log.info('Closing %s expired open pull requests.', prs.length);\n+\n+  _.each(prs, function(pr) {\n+    var repo = pr.head.repo.full_name;\n+    var client = RepoClientStore[repo];\n+    console.log('close!', client.github);\n+  });\n+};\n+\n+/**\n+ * Warn old expiring Pull Requests (older than 25 days)\n+ * @function\n+ * @param {Array} prs - List of Pull Request objects from GitHub API\n+ * @private\n+ */\n+var warnPrExpiring = function (prs) {\n+  log.info('Warning %s expiring open pull requests.', prs.length);\n+\n+  _.each(prs, function(pr) {\n+    var repo = pr.head.repo.full_name;\n+    var client = RepoClientStore[repo];\n+\n+console.log(pr.number, pr.user.login, repo);\n+\n+client.github.issues.getComments(","path":"cronjobs/pr-reviewer.js","position":270,"original_position":270,"commit_id":"272e7c77b35b2fe98d3d8ebca8dffbc8668d1195","original_commit_id":"272e7c77b35b2fe98d3d8ebca8dffbc8668d1195","user":{"login":"brev","id":208140,"avatar_url":"https://avatars.githubusercontent.com/u/208140?v=3","gravatar_id":"","url":"https://api.github.com/users/brev","html_url":"https://github.com/brev","followers_url":"https://api.github.com/users/brev/followers","following_url":"https://api.github.com/users/brev/following{/other_user}","gists_url":"https://api.github.com/users/brev/gists{/gist_id}","starred_url":"https://api.github.com/users/brev/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/brev/subscriptions","organizations_url":"https://api.github.com/users/brev/orgs","repos_url":"https://api.github.com/users/brev/repos","events_url":"https://api.github.com/users/brev/events{/privacy}","received_events_url":"https://api.github.com/users/brev/received_events","type":"User","site_admin":false},"body":"@rhyolight I've got access to github API object here, and am trying a simple `getComments` as a test, but it fails with no helpfulp error message. Look right to you in general? (Preparing to `createComment` here, see commented-out lines starting at 197","created_at":"2015-07-08T22:54:16Z","updated_at":"2015-07-08T22:54:16Z","html_url":"https://github.com/numenta/nupic.tools/pull/213#discussion_r34208094","pull_request_url":"https://api.github.com/repos/numenta/nupic.tools/pulls/213","_links":{"self":{"href":"https://api.github.com/repos/numenta/nupic.tools/pulls/comments/34208094"},"html":{"href":"https://github.com/numenta/nupic.tools/pull/213#discussion_r34208094"},"pull_request":{"href":"https://api.github.com/repos/numenta/nupic.tools/pulls/213"}}},"pull_request":{"url":"https://api.github.com/repos/numenta/nupic.tools/pulls/213","id":37745005,"html_url":"https://github.com/numenta/nupic.tools/pull/213","diff_url":"https://github.com/numenta/nupic.tools/pull/213.diff","patch_url":"https://github.com/numenta/nupic.tools/pull/213.patch","issue_url":"https://api.github.com/repos/numenta/nupic.tools/issues/213","number":213,"state":"open","locked":false,"title":"WIP! Initial work on #201, auto-closing old PRs.","user":{"login":"brev","id":208140,"avatar_url":"https://avatars.githubusercontent.com/u/208140?v=3","gravatar_id":"","url":"https://api.github.com/users/brev","html_url":"https://github.com/brev","followers_url":"https://api.github.com/users/brev/followers","following_url":"https://api.github.com/users/brev/following{/other_user}","gists_url":"https://api.github.com/users/brev/gists{/gist_id}","starred_url":"https://api.github.com/users/brev/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/brev/subscriptions","organizations_url":"https://api.github.com/users/brev/orgs","repos_url":"https://api.github.com/users/brev/repos","events_url":"https://api.github.com/users/brev/events{/privacy}","received_events_url":"https://api.github.com/users/brev/received_events","type":"User","site_admin":false},"body":"WIP! Initial work on #201, auto-closing old PRs.\r\n+ New dox.","created_at":"2015-06-15T23:06:57Z","updated_at":"2015-07-08T22:54:16Z","closed_at":null,"merged_at":null,"merge_commit_sha":"174b8253dd672789d91d917ab7358e1fbc0c54dc","assignee":{"login":"brev","id":208140,"avatar_url":"https://avatars.githubusercontent.com/u/208140?v=3","gravatar_id":"","url":"https://api.github.com/users/brev","html_url":"https://github.com/brev","followers_url":"https://api.github.com/users/brev/followers","following_url":"https://api.github.com/users/brev/following{/other_user}","gists_url":"https://api.github.com/users/brev/gists{/gist_id}","starred_url":"https://api.github.com/users/brev/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/brev/subscriptions","organizations_url":"https://api.github.com/users/brev/orgs","repos_url":"https://api.github.com/users/brev/repos","events_url":"https://api.github.com/users/brev/events{/privacy}","received_events_url":"https://api.github.com/users/brev/received_events","type":"User","site_admin":false},"milestone":null,"commits_url":"https://api.github.com/repos/numenta/nupic.tools/pulls/213/commits","review_comments_url":"https://api.github.com/repos/numenta/nupic.tools/pulls/213/comments","review_comment_url":"https://api.github.com/repos/numenta/nupic.tools/pulls/comments{/number}","comments_url":"https://api.github.com/repos/numenta/nupic.tools/issues/213/comments","statuses_url":"https://api.github.com/repos/numenta/nupic.tools/statuses/272e7c77b35b2fe98d3d8ebca8dffbc8668d1195","head":{"label":"brev:201-close-old-prs","ref":"201-close-old-prs","sha":"272e7c77b35b2fe98d3d8ebca8dffbc8668d1195","user":{"login":"brev","id":208140,"avatar_url":"https://avatars.githubusercontent.com/u/208140?v=3","gravatar_id":"","url":"https://api.github.com/users/brev","html_url":"https://github.com/brev","followers_url":"https://api.github.com/users/brev/followers","following_url":"https://api.github.com/users/brev/following{/other_user}","gists_url":"https://api.github.com/users/brev/gists{/gist_id}","starred_url":"https://api.github.com/users/brev/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/brev/subscriptions","organizations_url":"https://api.github.com/users/brev/orgs","repos_url":"https://api.github.com/users/brev/repos","events_url":"https://api.github.com/users/brev/events{/privacy}","received_events_url":"https://api.github.com/users/brev/received_events","type":"User","site_admin":false},"repo":{"id":23336130,"name":"nupic.tools","full_name":"brev/nupic.tools","owner":{"login":"brev","id":208140,"avatar_url":"https://avatars.githubusercontent.com/u/208140?v=3","gravatar_id":"","url":"https://api.github.com/users/brev","html_url":"https://github.com/brev","followers_url":"https://api.github.com/users/brev/followers","following_url":"https://api.github.com/users/brev/following{/other_user}","gists_url":"https://api.github.com/users/brev/gists{/gist_id}","starred_url":"https://api.github.com/users/brev/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/brev/subscriptions","organizations_url":"https://api.github.com/users/brev/orgs","repos_url":"https://api.github.com/users/brev/repos","events_url":"https://api.github.com/users/brev/events{/privacy}","received_events_url":"https://api.github.com/users/brev/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/brev/nupic.tools","description":"Server for tooling around the development process for the NuPIC project.","fork":true,"url":"https://api.github.com/repos/brev/nupic.tools","forks_url":"https://api.github.com/repos/brev/nupic.tools/forks","keys_url":"https://api.github.com/repos/brev/nupic.tools/keys{/key_id}","collaborators_url":"https://api.github.com/repos/brev/nupic.tools/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/brev/nupic.tools/teams","hooks_url":"https://api.github.com/repos/brev/nupic.tools/hooks","issue_events_url":"https://api.github.com/repos/brev/nupic.tools/issues/events{/number}","events_url":"https://api.github.com/repos/brev/nupic.tools/events","assignees_url":"https://api.github.com/repos/brev/nupic.tools/assignees{/user}","branches_url":"https://api.github.com/repos/brev/nupic.tools/branches{/branch}","tags_url":"https://api.github.com/repos/brev/nupic.tools/tags","blobs_url":"https://api.github.com/repos/brev/nupic.tools/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/brev/nupic.tools/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/brev/nupic.tools/git/refs{/sha}","trees_url":"https://api.github.com/repos/brev/nupic.tools/git/trees{/sha}","statuses_url":"https://api.github.com/repos/brev/nupic.tools/statuses/{sha}","languages_url":"https://api.github.com/repos/brev/nupic.tools/languages","stargazers_url":"https://api.github.com/repos/brev/nupic.tools/stargazers","contributors_url":"https://api.github.com/repos/brev/nupic.tools/contributors","subscribers_url":"https://api.github.com/repos/brev/nupic.tools/subscribers","subscription_url":"https://api.github.com/repos/brev/nupic.tools/subscription","commits_url":"https://api.github.com/repos/brev/nupic.tools/commits{/sha}","git_commits_url":"https://api.github.com/repos/brev/nupic.tools/git/commits{/sha}","comments_url":"https://api.github.com/repos/brev/nupic.tools/comments{/number}","issue_comment_url":"https://api.github.com/repos/brev/nupic.tools/issues/comments{/number}","contents_url":"https://api.github.com/repos/brev/nupic.tools/contents/{+path}","compare_url":"https://api.github.com/repos/brev/nupic.tools/compare/{base}...{head}","merges_url":"https://api.github.com/repos/brev/nupic.tools/merges","archive_url":"https://api.github.com/repos/brev/nupic.tools/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/brev/nupic.tools/downloads","issues_url":"https://api.github.com/repos/brev/nupic.tools/issues{/number}","pulls_url":"https://api.github.com/repos/brev/nupic.tools/pulls{/number}","milestones_url":"https://api.github.com/repos/brev/nupic.tools/milestones{/number}","notifications_url":"https://api.github.com/repos/brev/nupic.tools/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/brev/nupic.tools/labels{/name}","releases_url":"https://api.github.com/repos/brev/nupic.tools/releases{/id}","created_at":"2014-08-26T02:33:11Z","updated_at":"2015-06-12T18:00:48Z","pushed_at":"2015-07-08T22:50:04Z","git_url":"git://github.com/brev/nupic.tools.git","ssh_url":"git@github.com:brev/nupic.tools.git","clone_url":"https://github.com/brev/nupic.tools.git","svn_url":"https://github.com/brev/nupic.tools","homepage":null,"size":737,"stargazers_count":0,"watchers_count":0,"language":"JavaScript","has_issues":false,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"numenta:master","ref":"master","sha":"8d13b99b6ff5f8436b132d543e574274a2568636","user":{"login":"numenta","id":1039191,"avatar_url":"https://avatars.githubusercontent.com/u/1039191?v=3","gravatar_id":"","url":"https://api.github.com/users/numenta","html_url":"https://github.com/numenta","followers_url":"https://api.github.com/users/numenta/followers","following_url":"https://api.github.com/users/numenta/following{/other_user}","gists_url":"https://api.github.com/users/numenta/gists{/gist_id}","starred_url":"https://api.github.com/users/numenta/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/numenta/subscriptions","organizations_url":"https://api.github.com/users/numenta/orgs","repos_url":"https://api.github.com/users/numenta/repos","events_url":"https://api.github.com/users/numenta/events{/privacy}","received_events_url":"https://api.github.com/users/numenta/received_events","type":"Organization","site_admin":false},"repo":{"id":10611388,"name":"nupic.tools","full_name":"numenta/nupic.tools","owner":{"login":"numenta","id":1039191,"avatar_url":"https://avatars.githubusercontent.com/u/1039191?v=3","gravatar_id":"","url":"https://api.github.com/users/numenta","html_url":"https://github.com/numenta","followers_url":"https://api.github.com/users/numenta/followers","following_url":"https://api.github.com/users/numenta/following{/other_user}","gists_url":"https://api.github.com/users/numenta/gists{/gist_id}","starred_url":"https://api.github.com/users/numenta/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/numenta/subscriptions","organizations_url":"https://api.github.com/users/numenta/orgs","repos_url":"https://api.github.com/users/numenta/repos","events_url":"https://api.github.com/users/numenta/events{/privacy}","received_events_url":"https://api.github.com/users/numenta/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/numenta/nupic.tools","description":"Server for tooling around the development process for the NuPIC project.","fork":false,"url":"https://api.github.com/repos/numenta/nupic.tools","forks_url":"https://api.github.com/repos/numenta/nupic.tools/forks","keys_url":"https://api.github.com/repos/numenta/nupic.tools/keys{/key_id}","collaborators_url":"https://api.github.com/repos/numenta/nupic.tools/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/numenta/nupic.tools/teams","hooks_url":"https://api.github.com/repos/numenta/nupic.tools/hooks","issue_events_url":"https://api.github.com/repos/numenta/nupic.tools/issues/events{/number}","events_url":"https://api.github.com/repos/numenta/nupic.tools/events","assignees_url":"https://api.github.com/repos/numenta/nupic.tools/assignees{/user}","branches_url":"https://api.github.com/repos/numenta/nupic.tools/branches{/branch}","tags_url":"https://api.github.com/repos/numenta/nupic.tools/tags","blobs_url":"https://api.github.com/repos/numenta/nupic.tools/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/numenta/nupic.tools/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/numenta/nupic.tools/git/refs{/sha}","trees_url":"https://api.github.com/repos/numenta/nupic.tools/git/trees{/sha}","statuses_url":"https://api.github.com/repos/numenta/nupic.tools/statuses/{sha}","languages_url":"https://api.github.com/repos/numenta/nupic.tools/languages","stargazers_url":"https://api.github.com/repos/numenta/nupic.tools/stargazers","contributors_url":"https://api.github.com/repos/numenta/nupic.tools/contributors","subscribers_url":"https://api.github.com/repos/numenta/nupic.tools/subscribers","subscription_url":"https://api.github.com/repos/numenta/nupic.tools/subscription","commits_url":"https://api.github.com/repos/numenta/nupic.tools/commits{/sha}","git_commits_url":"https://api.github.com/repos/numenta/nupic.tools/git/commits{/sha}","comments_url":"https://api.github.com/repos/numenta/nupic.tools/comments{/number}","issue_comment_url":"https://api.github.com/repos/numenta/nupic.tools/issues/comments{/number}","contents_url":"https://api.github.com/repos/numenta/nupic.tools/contents/{+path}","compare_url":"https://api.github.com/repos/numenta/nupic.tools/compare/{base}...{head}","merges_url":"https://api.github.com/repos/numenta/nupic.tools/merges","archive_url":"https://api.github.com/repos/numenta/nupic.tools/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/numenta/nupic.tools/downloads","issues_url":"https://api.github.com/repos/numenta/nupic.tools/issues{/number}","pulls_url":"https://api.github.com/repos/numenta/nupic.tools/pulls{/number}","milestones_url":"https://api.github.com/repos/numenta/nupic.tools/milestones{/number}","notifications_url":"https://api.github.com/repos/numenta/nupic.tools/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/numenta/nupic.tools/labels{/name}","releases_url":"https://api.github.com/repos/numenta/nupic.tools/releases{/id}","created_at":"2013-06-10T23:11:35Z","updated_at":"2015-06-19T14:40:27Z","pushed_at":"2015-07-08T22:50:05Z","git_url":"git://github.com/numenta/nupic.tools.git","ssh_url":"git@github.com:numenta/nupic.tools.git","clone_url":"https://github.com/numenta/nupic.tools.git","svn_url":"https://github.com/numenta/nupic.tools","homepage":null,"size":1907,"stargazers_count":12,"watchers_count":12,"language":"JavaScript","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":10,"mirror_url":null,"open_issues_count":5,"forks":10,"open_issues":5,"watchers":12,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/numenta/nupic.tools/pulls/213"},"html":{"href":"https://github.com/numenta/nupic.tools/pull/213"},"issue":{"href":"https://api.github.com/repos/numenta/nupic.tools/issues/213"},"comments":{"href":"https://api.github.com/repos/numenta/nupic.tools/issues/213/comments"},"review_comments":{"href":"https://api.github.com/repos/numenta/nupic.tools/pulls/213/comments"},"review_comment":{"href":"https://api.github.com/repos/numenta/nupic.tools/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/numenta/nupic.tools/pulls/213/commits"},"statuses":{"href":"https://api.github.com/repos/numenta/nupic.tools/statuses/272e7c77b35b2fe98d3d8ebca8dffbc8668d1195"}}}}