mirror of
https://fastgit.cc/github.com/openclaw/openclaw
synced 2026-05-01 06:36:23 +08:00
fix(skills): use discussion target for notifications
Use the discussion node ID for discussion_comment notifications and update the skill docs to reflect the notify target semantics.
This commit is contained in:
@@ -64,16 +64,16 @@ The `fetch-content` output includes:
|
||||
|
||||
### Location type routing
|
||||
|
||||
| type | Flow |
|
||||
| ----------------------------- | ------------------------ |
|
||||
| `issue_comment` | Comment: delete+recreate |
|
||||
| `pull_request_comment` | Comment: delete+recreate |
|
||||
| `pull_request_review_comment` | Comment: delete+recreate |
|
||||
| type | Flow |
|
||||
| ----------------------------- | --------------------------------------------- |
|
||||
| `issue_comment` | Comment: delete+recreate |
|
||||
| `pull_request_comment` | Comment: delete+recreate |
|
||||
| `pull_request_review_comment` | Comment: delete+recreate |
|
||||
| `discussion_comment` | Discussion comment: delete+recreate (GraphQL) |
|
||||
| `issue_body` | Body: redact in place |
|
||||
| `pull_request_body` | Body: redact in place |
|
||||
| `commit` | Notify only |
|
||||
| _other_ | Skip and report |
|
||||
| `issue_body` | Body: redact in place |
|
||||
| `pull_request_body` | Body: redact in place |
|
||||
| `commit` | Notify only |
|
||||
| _other_ | Skip and report |
|
||||
|
||||
## Step 2: Decide (Agent)
|
||||
|
||||
@@ -102,6 +102,7 @@ node secret-scanning.mjs redact-body <issue|pr> <NUMBER> <redacted-body-file>
|
||||
### Comments — Delete and Recreate
|
||||
|
||||
For issue/PR comments:
|
||||
|
||||
```bash
|
||||
# Delete original (all edit history gone)
|
||||
node secret-scanning.mjs delete-comment <COMMENT_ID>
|
||||
@@ -111,6 +112,7 @@ node secret-scanning.mjs recreate-comment <ISSUE_NUMBER> <body-file>
|
||||
```
|
||||
|
||||
For discussion comments (uses GraphQL):
|
||||
|
||||
```bash
|
||||
# Delete original
|
||||
node secret-scanning.mjs delete-discussion-comment <COMMENT_NODE_ID>
|
||||
@@ -152,9 +154,12 @@ Cannot clean. Notify author to delete branch or force-push (for unmerged PRs).
|
||||
## Step 5: Notify
|
||||
|
||||
```bash
|
||||
node secret-scanning.mjs notify <ISSUE_NUMBER> <AUTHOR> <LOCATION_TYPE> <SECRET_TYPES>
|
||||
node secret-scanning.mjs notify <TARGET> <AUTHOR> <LOCATION_TYPE> <SECRET_TYPES>
|
||||
```
|
||||
|
||||
- For non-discussion types, `<TARGET>` is the issue/PR number.
|
||||
- For `discussion_comment`, `<TARGET>` is the `discussion_node_id` returned by `fetch-content`.
|
||||
|
||||
Secret types are comma-separated: `"Discord Bot Token,Feishu App Secret"`
|
||||
|
||||
The script picks the right template:
|
||||
|
||||
@@ -114,7 +114,8 @@ function cmdFetchContent(locationJson) {
|
||||
|
||||
while (hasNextPage && !comment) {
|
||||
const afterClause = cursor ? `, after: "${cursor}"` : "";
|
||||
const gql = ghGraphQL(`{
|
||||
const gql = ghGraphQL(
|
||||
`{
|
||||
repository(owner: "${owner}", name: "${name}") {
|
||||
discussion(number: ${discussionNumber}) {
|
||||
id
|
||||
@@ -131,10 +132,15 @@ function cmdFetchContent(locationJson) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}`, { allowFailure: true });
|
||||
}`,
|
||||
{ allowFailure: true },
|
||||
);
|
||||
|
||||
const discussion = gql?.data?.repository?.discussion;
|
||||
if (!discussion) fail(`Discussion #${discussionNumber} not found — it may have been deleted. The alert cannot be processed via this skill.`);
|
||||
if (!discussion)
|
||||
fail(
|
||||
`Discussion #${discussionNumber} not found — it may have been deleted. The alert cannot be processed via this skill.`,
|
||||
);
|
||||
|
||||
discussionId = discussion.id;
|
||||
comment = discussion.comments.nodes.find(
|
||||
@@ -144,7 +150,10 @@ function cmdFetchContent(locationJson) {
|
||||
cursor = discussion.comments.pageInfo.endCursor;
|
||||
}
|
||||
|
||||
if (!comment) fail(`Discussion comment #${discussionCommentDbId} not found in discussion #${discussionNumber}`);
|
||||
if (!comment)
|
||||
fail(
|
||||
`Discussion comment #${discussionCommentDbId} not found in discussion #${discussionNumber}`,
|
||||
);
|
||||
|
||||
const bodyFile = tmpFile("body.md");
|
||||
fs.writeFileSync(bodyFile, comment.body || "");
|
||||
@@ -356,7 +365,9 @@ function cmdDeleteComment(commentId) {
|
||||
*/
|
||||
function cmdDeleteDiscussionComment(nodeId) {
|
||||
if (!nodeId) fail("Usage: delete-discussion-comment <node-id>");
|
||||
const result = ghGraphQL(`mutation { deleteDiscussionComment(input: { id: "${nodeId}" }) { comment { id } } }`);
|
||||
const result = ghGraphQL(
|
||||
`mutation { deleteDiscussionComment(input: { id: "${nodeId}" }) { comment { id } } }`,
|
||||
);
|
||||
if (result?.errors) {
|
||||
fail(`Failed to delete discussion comment: ${JSON.stringify(result.errors)}`);
|
||||
}
|
||||
@@ -368,13 +379,20 @@ function cmdDeleteDiscussionComment(nodeId) {
|
||||
* Create a new discussion comment via GraphQL.
|
||||
*/
|
||||
function cmdRecreateDiscussionComment(discussionNodeId, bodyFile) {
|
||||
if (!discussionNodeId || !bodyFile) fail("Usage: recreate-discussion-comment <discussion-node-id> <body-file>");
|
||||
if (!discussionNodeId || !bodyFile)
|
||||
fail("Usage: recreate-discussion-comment <discussion-node-id> <body-file>");
|
||||
if (!fs.existsSync(bodyFile)) fail(`File not found: ${bodyFile}`);
|
||||
|
||||
const body = fs.readFileSync(bodyFile, "utf8");
|
||||
// Escape for GraphQL string literal
|
||||
const escapedBody = body.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\r/g, "\\r").replace(/\n/g, "\\n");
|
||||
const result = ghGraphQL(`mutation { addDiscussionComment(input: { discussionId: "${discussionNodeId}", body: "${escapedBody}" }) { comment { id url } } }`);
|
||||
const escapedBody = body
|
||||
.replace(/\\/g, "\\\\")
|
||||
.replace(/"/g, '\\"')
|
||||
.replace(/\r/g, "\\r")
|
||||
.replace(/\n/g, "\\n");
|
||||
const result = ghGraphQL(
|
||||
`mutation { addDiscussionComment(input: { discussionId: "${discussionNodeId}", body: "${escapedBody}" }) { comment { id url } } }`,
|
||||
);
|
||||
if (result?.errors) {
|
||||
fail(`Failed to create discussion comment: ${JSON.stringify(result.errors)}`);
|
||||
}
|
||||
@@ -415,12 +433,13 @@ function cmdRecreateComment(issueNumber, bodyFile) {
|
||||
}
|
||||
|
||||
/**
|
||||
* notify <issue-or-pr-number> <author> <location-type> <secret-types>
|
||||
* notify <target> <author> <location-type> <secret-types>
|
||||
* Post a notification comment with the correct template for the location type.
|
||||
* target = issue/PR number for non-discussion types, discussion node ID for discussion_comment.
|
||||
*/
|
||||
function cmdNotify(issueNumber, author, locationType, secretTypes) {
|
||||
if (!issueNumber || !author || !locationType || !secretTypes) {
|
||||
fail("Usage: notify <issue-or-pr-number> <author> <location-type> <secret-types-comma-sep>");
|
||||
function cmdNotify(target, author, locationType, secretTypes) {
|
||||
if (!target || !author || !locationType || !secretTypes) {
|
||||
fail("Usage: notify <target> <author> <location-type> <secret-types-comma-sep>");
|
||||
}
|
||||
|
||||
const types = secretTypes.split(",").map((s) => s.trim());
|
||||
@@ -468,8 +487,14 @@ function cmdNotify(issueNumber, author, locationType, secretTypes) {
|
||||
|
||||
// Discussion comments must be notified via GraphQL
|
||||
if (locationType === "discussion_comment") {
|
||||
const escapedBody = body.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\r/g, "\\r").replace(/\n/g, "\\n");
|
||||
const result = ghGraphQL(`mutation { addDiscussionComment(input: { discussionId: "${issueNumber}", body: "${escapedBody}" }) { comment { id url } } }`);
|
||||
const escapedBody = body
|
||||
.replace(/\\/g, "\\\\")
|
||||
.replace(/"/g, '\\"')
|
||||
.replace(/\r/g, "\\r")
|
||||
.replace(/\n/g, "\\n");
|
||||
const result = ghGraphQL(
|
||||
`mutation { addDiscussionComment(input: { discussionId: "${target}", body: "${escapedBody}" }) { comment { id url } } }`,
|
||||
);
|
||||
if (result?.errors) {
|
||||
fail(`Failed to post discussion notification: ${JSON.stringify(result.errors)}`);
|
||||
}
|
||||
@@ -490,7 +515,7 @@ function cmdNotify(issueNumber, author, locationType, secretTypes) {
|
||||
|
||||
const result = gh([
|
||||
"api",
|
||||
`repos/${REPO}/issues/${issueNumber}/comments`,
|
||||
`repos/${REPO}/issues/${target}/comments`,
|
||||
"-X",
|
||||
"POST",
|
||||
"-F",
|
||||
@@ -660,7 +685,7 @@ if (!command || !commands[command]) {
|
||||
" delete-discussion-comment <node-id> Delete a discussion comment (GraphQL)",
|
||||
" recreate-comment <issue-n> <file> Create replacement comment",
|
||||
" recreate-discussion-comment <disc-node-id> <file> Create discussion comment (GraphQL)",
|
||||
" notify <n> <author> <type> <types> Post notification",
|
||||
" notify <target> <author> <type> <types> Post notification",
|
||||
" resolve <n> [resolution] [comment] Close alert",
|
||||
" list-open List open alerts",
|
||||
" summary <json-file> Print formatted summary",
|
||||
|
||||
Reference in New Issue
Block a user