# ticket-notify

A tiny Go CLI that, on each invocation:

1. Queries Jira for tickets in a set of projects that were **updated in the last N minutes**.
2. Asks **Gemini** which of them need special attention (per your free-text criteria).
3. Pushes the flagged ones to a **Lark group** via webhook — but only if they haven't already been pushed for that same ticket update.

Designed to be run from `cron` every few minutes.

## Build

```sh
cd /Users/wk/lc/ticket-notify
go build -o ticket-notify .
```

## Configure

Copy the example and fill it in:

```sh
cp config.example.json config.json
$EDITOR config.json
```

Fields:

| field | meaning |
|---|---|
| `jira_base_url` | e.g. `https://your-domain.atlassian.net` |
| `jira_email` / `jira_token` | Atlassian account email + API token (https://id.atlassian.com/manage-profile/security/api-tokens) |
| `jira_projects` | list of project keys, e.g. `["PROJ1","PROJ2"]` |
| `lookback_minutes` | JQL window, default `60` — should be ≥ your cron interval |
| `lark_webhook` | Lark custom-bot webhook URL |
| `gemini_api_key` | Google AI Studio API key (https://aistudio.google.com/apikey) |
| `gemini_model` | defaults to `gemini-2.5-flash` |
| `attention_criteria` | plain English description of what counts as "needs attention" |
| `state_file` | dedup DB path, default `/tmp/ticket-notify-state.json` |

## Run

```sh
./ticket-notify -config ./config.json           # normal
./ticket-notify -config ./config.json -dry-run  # print, don't push to Lark
```

## Cron

Run every 10 minutes with a 60-minute lookback — the dedup state prevents double-notifying:

```cron
*/10 * * * * /Users/wk/lc/ticket-notify/ticket-notify -config /Users/wk/lc/ticket-notify/config.json >> /tmp/ticket-notify.log 2>&1
```

Tip: make the cron interval ≤ `lookback_minutes` so that a single failed run doesn't cause you to miss tickets.

## Dedup behavior

State is keyed by `ticketKey|updatedTimestamp`:

- The same ticket, same update → notified at most once.
- The same ticket, new update → treated as a new event and re-evaluated by Claude.
- Entries older than 7 days are pruned automatically.
- State is only persisted **after** a successful Lark push, so a Lark failure will retry next run.

## Files

- `main.go` — orchestration
- `config.go` — config loading
- `jira.go` — Jira REST v3 search
- `filter.go` — Gemini `generateContent` API call
- `lark.go` — Lark interactive-card webhook
- `state.go` — JSON-file dedup store
