<commit_before>/**
 * Copyright 2017 Google Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import { BotConfig } from "./config";
import * as github from "./github";
import * as log from "./log";
import * as util from "./util";
import * as types from "./types";

// Metadata the bot can leave in comments to mark its actions
const EVT_MARK_STALE = "event: mark-stale";
const EVT_CLOSE_STALE = "event: close-stale";

const DAY_MS = 24 * 60 * 60 * 1000;

/**
 * Create a new handler for cron-style tasks.
 * @param {GithubClient} gh_client client for accessing Github.
 */
export class CronHandler {
  gh_client: github.GithubClient;
  config: BotConfig;

  constructor(gh_client: github.GithubClient, config: BotConfig) {
    this.gh_client = gh_client;
    this.config = config;
  }

  async processIssues(org: string, name: string) {
    log.debug(`Processing issues for ${org}/${name}`);

    // Get the configuration for this repo
    const cleanupConfig = this.config.getRepoCleanupConfig(org, name);
    if (!cleanupConfig || !cleanupConfig.issue) {
      log.debug(`No issue cleanup config for ${org}/${name}`);
      return [];
    }
    const issueConfig = cleanupConfig.issue;

    const lockActions = await this.handleClosedIssues(org, name, issueConfig);
    const staleActions = await this.handleStaleIssues(org, name, issueConfig);

    return [...lockActions, ...staleActions];
  }

  async handleClosedIssues(
    org: string,
    name: string,
    issueConfig: types.IssueCleanupConfig
  ): Promise<types.Action[]> {
    if (!issueConfig.lock_days) {
      log.debug(`No issue locking config for ${org}/${name}`);
      return [];
    }

    const lockDays = issueConfig.lock_days;
    const lockMillis = lockDays * 24 * 60 * 60 * 1000;

    const actions: types.Action[] = [];

    const issues = await this.gh_client.getIssuesForRepo(org, name, "closed");
    for (const issue of issues) {
      if (!issue.closed_at) {
        log.warn(
          `Closed issue ${org}/${name}/${issue.number} has no closed_at.`
        );
        continue;
      }

      const closedAtStr = "" + issue.closed_at;
      const closedAtMs = new Date(closedAtStr).getTime();
      const nowMs = new Date().getTime();

      if (nowMs - closedAtMs > lockMillis) {
        actions.push(
          new types.GithubLockAction(
            org,
            name,
            issue.number,
            `Issue was closed at ${closedAtStr} which is more than ${lockDays} ago`
          )
        );
      }
    }

    return actions;
  }<commit_msg>Review comments<commit_after>/**
 * Copyright 2017 Google Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import { BotConfig } from "./config";
import * as github from "./github";
import * as log from "./log";
import * as util from "./util";
import * as types from "./types";

// Metadata the bot can leave in comments to mark its actions
const EVT_MARK_STALE = "event: mark-stale";
const EVT_CLOSE_STALE = "event: close-stale";

const DAY_MS = 24 * 60 * 60 * 1000;

/**
 * Create a new handler for cron-style tasks.
 * @param {GithubClient} gh_client client for accessing Github.
 */
export class CronHandler {
  gh_client: github.GithubClient;
  config: BotConfig;

  constructor(gh_client: github.GithubClient, config: BotConfig) {
    this.gh_client = gh_client;
    this.config = config;
  }

  async processIssues(org: string, name: string) {
    log.debug(`Processing issues for ${org}/${name}`);

    // Get the configuration for this repo
    const cleanupConfig = this.config.getRepoCleanupConfig(org, name);
    if (!cleanupConfig || !cleanupConfig.issue) {
      log.debug(`No issue cleanup config for ${org}/${name}`);
      return [];
    }
    const issueConfig = cleanupConfig.issue;

    const lockActions = await this.handleClosedIssues(org, name, issueConfig);
    const staleActions = await this.handleStaleIssues(org, name, issueConfig);

    return [...lockActions, ...staleActions];
  }

  async handleClosedIssues(
    org: string,
    name: string,
    issueConfig: types.IssueCleanupConfig
  ): Promise<types.Action[]> {
    if (!issueConfig.lock_days) {
      log.debug(`No issue locking config for ${org}/${name}`);
      return [];
    }

    const lockDays = issueConfig.lock_days;
    const lockMillis = lockDays * 24 * 60 * 60 * 1000;

    const actions: types.Action[] = [];

    const issues = await this.gh_client.getIssuesForRepo(org, name, "closed");
    const nowMs = new Date().getTime();

    for (const issue of issues) {
      // This is a "this should never happen" case but the GitHub API
      // is not type-safe enough to ignore the possibility.
      if (!issue.closed_at) {
        log.warn(
          `Closed issue ${org}/${name}/${issue.number} has no closed_at.`
        );
        continue;
      }

      const closedAtStr = "" + issue.closed_at;
      const closedAtMs = new Date(closedAtStr).getTime();

      if (nowMs - closedAtMs > lockMillis) {
        actions.push(
          new types.GithubLockAction(
            org,
            name,
            issue.number,
            `Issue was closed at ${closedAtStr} which is more than ${lockDays} ago`
          )
        );
      }
    }

    return actions;
  }
{'fraction_non_alphanumeric': 0.11310563939634631, 'fraction_numerical': 0.008895949166004766, 'mean_word_length': 2.908131595282433, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 7, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6679391', 'n_tokens_mistral': 1899, 'n_tokens_neox': 1797, 'n_words': 615}