mirror of
https://github.com/openclaw/openclaw.git
synced 2026-02-19 16:47:29 +00:00
225 lines
8.1 KiB
YAML
225 lines
8.1 KiB
YAML
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",
|
||
});
|
||
}
|