name: Auto response

on:
  issues:
    types: [opened, edited, labeled]
  pull_request_target:
    types: [labeled]

permissions: {}

jobs:
  auto-response:
    permissions:
      issues: write
      pull-requests: write
    runs-on: ubuntu-latest
    steps:
      - uses: actions/create-github-app-token@d72941d797fd3113feb6b93fd0dec494b13a2547 # v1
        id: app-token
        with:
          app-id: "2729701"
          private-key: ${{ secrets.GH_APP_PRIVATE_KEY }}
      - name: Handle labeled items
        uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7
        with:
          github-token: ${{ steps.app-token.outputs.token }}
          script: |
            // Labels prefixed with "r:" are auto-response triggers.
            const rules = [
              {
                label: "r: skill",
                close: true,
                message:
                  "Thanks for the contribution! New skills should be published to [Clawhub](https://clawhub.ai) for everyone to use. We’re keeping the core lean on skills, so I’m closing this out.",
              },
              {
                label: "r: support",
                close: true,
                message:
                  "Please use [our support server](https://discord.gg/clawd) and ask in #help or #users-helping-users to resolve this, or follow the stuck FAQ at https://docs.openclaw.ai/help/faq#im-stuck-whats-the-fastest-way-to-get-unstuck.",
              },
              {
                label: "r: testflight",
                close: true,
                message: "Not available, build from source.",
              },
              {
                label: "r: third-party-extension",
                close: true,
                message:
                  "This would be better made as a third-party extension with our SDK that you maintain yourself. Docs: https://docs.openclaw.ai/plugin.",
              },
              {
                label: "r: moltbook",
                close: true,
                lock: true,
                lockReason: "off-topic",
                message:
                  "OpenClaw is not affiliated with Moltbook, and issues related to Moltbook should not be submitted here.",
              },
            ];

            const triggerLabel = "trigger-response";
            const target = context.payload.issue ?? context.payload.pull_request;
            if (!target) {
              return;
            }

            const labelSet = new Set(
              (target.labels ?? [])
                .map((label) => (typeof label === "string" ? label : label?.name))
                .filter((name) => typeof name === "string"),
            );

            const hasTriggerLabel = labelSet.has(triggerLabel);
            if (hasTriggerLabel) {
              labelSet.delete(triggerLabel);
              try {
                await github.rest.issues.removeLabel({
                  owner: context.repo.owner,
                  repo: context.repo.repo,
                  issue_number: target.number,
                  name: triggerLabel,
                });
              } catch (error) {
                if (error?.status !== 404) {
                  throw error;
                }
              }
            }

            const isLabelEvent = context.payload.action === "labeled";
            if (!hasTriggerLabel && !isLabelEvent) {
              return;
            }

            const issue = context.payload.issue;
            if (issue) {
              const title = issue.title ?? "";
              const body = issue.body ?? "";
              const haystack = `${title}\n${body}`.toLowerCase();
              const hasMoltbookLabel = labelSet.has("r: moltbook");
              const hasTestflightLabel = labelSet.has("r: testflight");
              const hasSecurityLabel = labelSet.has("security");
              if (title.toLowerCase().includes("security") && !hasSecurityLabel) {
                await github.rest.issues.addLabels({
                  owner: context.repo.owner,
                  repo: context.repo.repo,
                  issue_number: issue.number,
                  labels: ["security"],
                });
                labelSet.add("security");
              }
              if (title.toLowerCase().includes("testflight") && !hasTestflightLabel) {
                await github.rest.issues.addLabels({
                  owner: context.repo.owner,
                  repo: context.repo.repo,
                  issue_number: issue.number,
                  labels: ["r: testflight"],
                });
                labelSet.add("r: testflight");
              }
              if (haystack.includes("moltbook") && !hasMoltbookLabel) {
                await github.rest.issues.addLabels({
                  owner: context.repo.owner,
                  repo: context.repo.repo,
                  issue_number: issue.number,
                  labels: ["r: moltbook"],
                });
                labelSet.add("r: moltbook");
              }
            }

            const invalidLabel = "invalid";
            const dirtyLabel = "dirty";
            const noisyPrMessage =
              "Closing this PR because it looks dirty (too many unrelated commits). Please recreate the PR from a clean branch.";

            const pullRequest = context.payload.pull_request;
            if (pullRequest) {
              if (labelSet.has(dirtyLabel)) {
                await github.rest.issues.createComment({
                  owner: context.repo.owner,
                  repo: context.repo.repo,
                  issue_number: pullRequest.number,
                  body: noisyPrMessage,
                });
                await github.rest.issues.update({
                  owner: context.repo.owner,
                  repo: context.repo.repo,
                  issue_number: pullRequest.number,
                  state: "closed",
                });
                return;
              }
              const labelCount = labelSet.size;
              if (labelCount > 20) {
                await github.rest.issues.createComment({
                  owner: context.repo.owner,
                  repo: context.repo.repo,
                  issue_number: pullRequest.number,
                  body: noisyPrMessage,
                });
                await github.rest.issues.update({
                  owner: context.repo.owner,
                  repo: context.repo.repo,
                  issue_number: pullRequest.number,
                  state: "closed",
                });
                return;
              }
              if (labelSet.has(invalidLabel)) {
                await github.rest.issues.update({
                  owner: context.repo.owner,
                  repo: context.repo.repo,
                  issue_number: pullRequest.number,
                  state: "closed",
                });
                return;
              }
            }

            if (issue && labelSet.has(invalidLabel)) {
              await github.rest.issues.update({
                owner: context.repo.owner,
                repo: context.repo.repo,
                issue_number: issue.number,
                state: "closed",
                state_reason: "not_planned",
              });
              return;
            }

            const rule = rules.find((item) => labelSet.has(item.label));
            if (!rule) {
              return;
            }

            const issueNumber = target.number;

            await github.rest.issues.createComment({
              owner: context.repo.owner,
              repo: context.repo.repo,
              issue_number: issueNumber,
              body: rule.message,
            });

            if (rule.close) {
              await github.rest.issues.update({
                owner: context.repo.owner,
                repo: context.repo.repo,
                issue_number: issueNumber,
                state: "closed",
              });
            }

            if (rule.lock) {
              await github.rest.issues.lock({
                owner: context.repo.owner,
                repo: context.repo.repo,
                issue_number: issueNumber,
                lock_reason: rule.lockReason ?? "resolved",
              });
            }
