update truncation

This commit is contained in:
2026-03-11 01:29:55 -04:00
parent d4731dc179
commit 03791ecca8
2 changed files with 137 additions and 5 deletions

132
AGENTS.md Normal file
View File

@@ -0,0 +1,132 @@
# Agent Guide for github-to-signal
HTTP server that receives GitHub webhook events and forwards them as Signal messages via signal-cli.
## Commands
```bash
# Build
GOWORK=off go build -o github-to-signal .
# Test
GOWORK=off go test .
# Run (requires config.toml)
./github-to-signal
```
**Note**: This repo may not be in a parent `go.work` workspace. Use `GOWORK=off` to ensure commands work standalone.
## Project Structure
```
.
├── main.go # Entry point, HTTP server, event handlers
├── config.go # Configuration loading (TOML + env vars)
├── filter.go # Event filtering logic
├── filter_test.go # Tests for event filtering
├── format.go # Message formatting for each event type
├── config.example.toml
└── deploy/ # Systemd services and nginx config
```
All source files are in the root directory (single `main` package, no subdirectories).
## Configuration
Configuration via `config.toml` or environment variables with `GH2SIG_` prefix:
| Config Key | Env Variable | Description |
|------------|--------------|-------------|
| `webhook_secret` | `GH2SIG_WEBHOOK_SECRET` | GitHub webhook secret |
| `listen_addr` | `GH2SIG_LISTEN_ADDR` | Server address (default `:9900`) |
| `signal_url` | `GH2SIG_SIGNAL_URL` | signal-cli JSON-RPC endpoint |
| `signal_account` | `GH2SIG_SIGNAL_ACCOUNT` | Phone number for signal-cli |
| `signal_recipient` | `GH2SIG_SIGNAL_RECIPIENT` | Recipient UUID for DMs |
| `signal_group_id` | `GH2SIG_SIGNAL_GROUP_ID` | Group ID (overrides recipient) |
| `events` | `GH2SIG_EVENTS` | Comma-separated event filter |
Configuration is loaded using [jety](https://github.com/taigrr/jety) library.
## Code Patterns
### Event Handlers
Each GitHub event type has:
1. A handler method on `notifier` struct in `main.go`
2. A `format*` function in `format.go` that returns the Signal message
Handler pattern:
```go
func (n *notifier) onEventName(ctx context.Context, _ string, _ string, event *github.EventType) error {
if !n.filter.Allowed("event_name", event.GetAction()) {
return nil
}
n.send(ctx, formatEventName(event))
return nil
}
```
### Event Filtering
`EventFilter` in `filter.go` supports:
- Empty filter = allow all events
- `"event"` = all actions of that event type
- `"event:action"` = specific action only
Two-level check:
1. `EventEnabled(event)` — used at registration time to skip registering handlers
2. `Allowed(event, action)` — checked at runtime for action-level filtering
### Message Formatting
All `format*` functions in `format.go`:
- Return a string (empty string = no message sent)
- Use `[repo] user action ...` prefix format
- Include relevant URLs
- Truncate bodies with `truncate()` helper
### Dependencies
- `cbrgm/githubevents` — GitHub webhook event parsing and routing
- `google/go-github` — GitHub API types
- `taigrr/signalcli` — signal-cli JSON-RPC client
- `taigrr/jety` — Configuration (TOML/JSON/YAML/env)
## Adding New Event Types
1. Add handler method in `main.go` following existing pattern
2. Register handler in `main()` with `EventEnabled` check
3. Add `format*` function in `format.go`
4. Add event name to filter docs in `config.example.toml`
## Testing
Only `filter_test.go` exists — table-driven tests for `EventFilter`.
```bash
GOWORK=off go test -v .
```
## HTTP Endpoints
| Path | Method | Description |
|------|--------|-------------|
| `/webhook` | POST | GitHub webhook receiver |
| `/health` | GET | Health check (returns `ok`) |
## Deployment
Systemd services in `deploy/`:
- `signal-cli-bot.service` — runs signal-cli daemon
- `github-to-signal.service` — runs this server (depends on signal-cli)
- `github-to-signal.nginx.conf` — nginx reverse proxy config
The server expects signal-cli to be running on `127.0.0.1:8081`.
## Gotchas
- **GOWORK**: May need `GOWORK=off` if a parent `go.work` exists
- **signal-cli port**: Default in code is `8080`, but deployment uses `8081` to avoid conflicts
- **Workflow runs**: Only notifies on `completed` action, ignores `requested`/`in_progress`
- **Empty message**: Returning `""` from a formatter skips sending (used by workflow_run filter)

View File

@@ -44,7 +44,7 @@ func formatIssue(event *github.IssuesEvent) string {
repo, sender, action, issue.GetNumber(), issue.GetTitle(), issue.GetHTMLURL())
if action == "opened" && issue.GetBody() != "" {
body := truncate(issue.GetBody(), 200)
body := truncate(issue.GetBody(), 2000)
msg += "\n\n" + body
}
@@ -58,7 +58,7 @@ func formatIssueComment(event *github.IssueCommentEvent) string {
issue := event.GetIssue()
comment := event.GetComment()
body := truncate(comment.GetBody(), 300)
body := truncate(comment.GetBody(), 2000)
return fmt.Sprintf("[%s] %s commented on #%d (%s):\n%s\n%s",
repo, sender, issue.GetNumber(), issue.GetTitle(), body, comment.GetHTMLURL())
@@ -75,7 +75,7 @@ func formatPR(event *github.PullRequestEvent) string {
repo, sender, action, pr.GetNumber(), pr.GetTitle(), pr.GetHTMLURL())
if action == "opened" && pr.GetBody() != "" {
body := truncate(pr.GetBody(), 200)
body := truncate(pr.GetBody(), 2000)
msg += "\n\n" + body
}
@@ -90,7 +90,7 @@ func formatPRReview(event *github.PullRequestReviewEvent) string {
review := event.GetReview()
state := review.GetState()
body := truncate(review.GetBody(), 200)
body := truncate(review.GetBody(), 2000)
msg := fmt.Sprintf("[%s] %s %s PR #%d: %s\n%s",
repo, sender, state, pr.GetNumber(), pr.GetTitle(), review.GetHTMLURL())
@@ -109,7 +109,7 @@ func formatPRReviewComment(event *github.PullRequestReviewCommentEvent) string {
pr := event.GetPullRequest()
comment := event.GetComment()
body := truncate(comment.GetBody(), 300)
body := truncate(comment.GetBody(), 2000)
return fmt.Sprintf("[%s] %s commented on PR #%d (%s):\n%s\n%s",
repo, sender, pr.GetNumber(), pr.GetTitle(), body, comment.GetHTMLURL())