feat(jira): watch_issues reports what changed on a set of tickets (1.4.0)

Given up to 50 keys and an instant, returns the issues updated since with
their status, new comments (author id, plain-text body, own comments
flagged) and status transitions, plus the instance's own account. JQL dates
are converted to the account's time zone; malformed keys are never queried;
a deleted key falls back to per-key reads instead of failing the batch.

get_comments gains order, start_at and author_id, and decodes HTML entities.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-13 15:59:42 +00:00
parent e973ccb673
commit 725e6921f4
3 changed files with 456 additions and 8 deletions
+28 -4
View File
@@ -142,24 +142,46 @@ def adf(text):
"content": [{"type": "paragraph", "content": [{"type": "text", "text": text}]}]}
def _html_text(rendered):
t = re.sub(r"(?i)<br\s*/?>", "\n", str(rendered))
t = re.sub(r"(?i)</(p|div|li|h[1-6]|tr|blockquote|pre)>", "\n", t)
t = re.sub(r"<[^>]+>", "", t)
t = html.unescape(t)
t = re.sub(r"[ \t\r\f\v]+\n", "\n", t)
return re.sub(r"\n{3,}", "\n\n", t).strip()
def _body_text(comment):
rendered = comment.get("renderedBody")
if rendered:
return re.sub(r"<[^>]+>", "", str(rendered))
return _html_text(rendered)
raw_body = comment.get("body")
if isinstance(raw_body, str):
return raw_body
return ""
def _user_id(u):
# Cloud identifies a user by accountId; On-Prem by key, then name.
u = u or {}
return str(u.get("accountId") or u.get("key") or u.get("name") or "")
def main():
inputs = json.loads(os.environ.get("INTEGRATION_INPUTS", "{}"))
raw_ref = str(inputs.get("issue_id") or "") or str(inputs.get("issue_key") or "")
ref = issue_ref(inputs)
limit = inputs.get("limit") or 50
order = str(inputs.get("order") or "oldest").strip().lower()
if order not in ("oldest", "newest"):
raise Exception("order is oldest or newest, not " + order)
start_at = inputs.get("start_at")
start_at = int(start_at) if str(start_at or "").strip() != "" else 0
res = request("GET", "/rest/api/" + _api() + "/issue/" + ref + "/comment",
params={"maxResults": limit, "expand": "renderedBody"})
params={"maxResults": limit, "startAt": start_at,
"orderBy": "-created" if order == "newest" else "created",
"expand": "renderedBody"})
comments = []
for c in res.get("comments", []) or []:
@@ -167,13 +189,15 @@ def main():
"id": c.get("id", ""),
"body": _body_text(c),
"author": c.get("author", {}).get("displayName", ""),
"author_id": _user_id(c.get("author")),
"created": c.get("created", ""),
"updated": c.get("updated", ""),
"update_author": c.get("updateAuthor", {}).get("displayName", ""),
})
print(json.dumps({"issue": raw_ref, "comments": comments}))
total = int(res.get("total") or 0)
print(json.dumps({"issue": raw_ref, "comments": comments, "total": total,
"next_start_at": start_at + len(comments) if start_at + len(comments) < total else None}))
try:
main()