yaju-is-always-watching-you/internal/forgejo/client.go
awayatan f47fef177b
Some checks failed
CI / test (push) Failing after 4s
Add yaju-is-always-watching-you: Forgejo goroku-check webhook bot
Watches issue/PR comments and pushes for flagged phrases; warns commenters
via reply and opens a redacted correction PR for flagged file content,
never rewriting history or deleting content directly.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-26 22:47:12 +09:00

87 lines
2.6 KiB
Go

// Package forgejo wraps the parts of the Gitea/Forgejo SDK that
// yaju-keisatsu needs: replying to comments and opening a correction PR.
package forgejo
import (
"encoding/base64"
"fmt"
"strings"
"code.gitea.io/sdk/gitea"
)
type Client struct {
sdk *gitea.Client
}
func New(baseURL, token string) (*Client, error) {
c, err := gitea.NewClient(baseURL, gitea.SetToken(token))
if err != nil {
return nil, fmt.Errorf("create gitea client: %w", err)
}
return &Client{sdk: c}, nil
}
// ReplyToIssue posts a comment on an issue or pull request.
func (c *Client) ReplyToIssue(owner, repo string, index int64, body string) error {
_, _, err := c.sdk.CreateIssueComment(owner, repo, index, gitea.CreateIssueCommentOption{Body: body})
if err != nil {
return fmt.Errorf("create issue comment: %w", err)
}
return nil
}
// FileContent fetches a file's current text content and blob SHA at ref.
func (c *Client) FileContent(owner, repo, ref, path string) (content, sha string, err error) {
meta, _, err := c.sdk.GetContents(owner, repo, ref, path)
if err != nil {
return "", "", fmt.Errorf("get file %s: %w", path, err)
}
if meta.Content == nil {
return "", "", fmt.Errorf("get file %s: not a regular file", path)
}
// The API may line-wrap the base64 payload; strip whitespace before decoding.
clean := strings.Map(func(r rune) rune {
if r == '\n' || r == '\r' {
return -1
}
return r
}, *meta.Content)
decoded, err := base64.StdEncoding.DecodeString(clean)
if err != nil {
return "", "", fmt.Errorf("decode file %s: %w", path, err)
}
return string(decoded), meta.SHA, nil
}
// CommitCorrection commits newContent for path on a new branch (branched off
// baseBranch) without touching baseBranch itself.
func (c *Client) CommitCorrection(owner, repo, baseBranch, newBranch, path, sha, newContent, message string) error {
_, _, err := c.sdk.UpdateFile(owner, repo, path, gitea.UpdateFileOptions{
SHA: sha,
Content: base64.StdEncoding.EncodeToString([]byte(newContent)),
FileOptions: gitea.FileOptions{
Message: message,
BranchName: baseBranch,
NewBranchName: newBranch,
},
})
if err != nil {
return fmt.Errorf("update file %s: %w", path, err)
}
return nil
}
// OpenCorrectionPR opens a pull request from head into base.
func (c *Client) OpenCorrectionPR(owner, repo, head, base, title, body string) error {
_, _, err := c.sdk.CreatePullRequest(owner, repo, gitea.CreatePullRequestOption{
Head: head,
Base: base,
Title: title,
Body: body,
})
if err != nil {
return fmt.Errorf("create pull request: %w", err)
}
return nil
}