GitHub PR Review Assistant with OpenClaw: From Webhook to Telegram

Too many PRs, too little time on a phone screen. This lab note walks through the loop I use: GitHub opens a PR → a tiny webhook receiver → OpenClaw summarizes the diff → Telegram gets a short, actionable digest.

Lab note by the maintainer. Prerequisites: working OpenClaw + Telegram. Harden tokens with security best practices. Secrets = XXXX.

1. Why I built this

On multi-repo weeks I miss PR noise until CI is red. I wanted:

  • A push when a PR opens or updates (not every comment storm).
  • A 8–15 line summary: intent, risky files, tests to run—not a novel.
  • Everything self-hosted so repo tokens never sit in a random SaaS bot.

This is a triage assistant, not a replacement for human review.

2. Architecture

GitHub (pull_request event)
    → HTTPS webhook (small Node/Express or Python receiver on VPS)
        → verify signature
        → extract title, author, URL, changed files, patch summary
        → call OpenClaw agent (CLI or HTTP)
            → Telegram channel message

Components:

  • GitHub fine-grained PAT or GitHub App installation with read access to PRs/contents.
  • Webhook receiver (only this port is public; Gateway stays on localhost).
  • OpenClaw agent PrReviewBrief with a strict system prompt.
  • Telegram bot for delivery (setup).

3. Prerequisites

  • OpenClaw running on a VPS or always-on box — VPS / hub
  • Telegram bot connected
  • GitHub repo admin rights to add a webhook
  • TLS for the webhook URL (Caddy/Nginx) — do not send webhook traffic to the Gateway directly
export GITHUB_TOKEN="XXXX"
export GITHUB_WEBHOOK_SECRET="XXXX"
export TELEGRAM_BOT_TOKEN="XXXX"
export TELEGRAM_CHAT_ID="XXXX"

4. Define the PR review agent

{
  "agents": [
    {
      "name": "PrReviewBrief",
      "description": "Summarise GitHub pull requests for Telegram triage.",
      "model": "gpt-4.1-mini",
      "skills": ["github_pr_read"],
      "system_prompt": "You summarise pull requests for a busy developer.\nOutput:\n1) One-line intent\n2) Risk files (max 5)\n3) Missing tests / concerns (max 3)\n4) Suggested next action (approve / request changes / needs human)\nMax 180 words. No code dumps. No secrets."
    }
  ]
}

Use a mid/cheap model for routine PRs; escalate to a stronger model only for large or security-sensitive diffs (LLM guide, cost playbook).

5. Minimal webhook receiver (Node sketch)

This is intentionally small and replaceable. Verify signatures; never trust raw JSON.

// webhook-server.js (sketch)
import express from "express";
import crypto from "crypto";
import { execFile } from "child_process";

const app = express();
app.post("/github/webhook", express.raw({ type: "*/*" }), (req, res) => {
  const sig = req.headers["x-hub-signature-256"];
  const digest = "sha256=" + crypto
    .createHmac("sha256", process.env.GITHUB_WEBHOOK_SECRET)
    .update(req.body)
    .digest("hex");
  if (sig !== digest) return res.status(401).end();

  const event = JSON.parse(req.body.toString("utf8"));
  if (event.action !== "opened" && event.action !== "synchronize") {
    return res.status(204).end();
  }

  const pr = event.pull_request;
  const payload = JSON.stringify({
    title: pr.title,
    number: pr.number,
    url: pr.html_url,
    author: pr.user.login,
    body: (pr.body || "").slice(0, 2000)
  });

  // Official CLI: https://docs.openclaw.ai/cli/agent
  execFile("openclaw", [
    "agent",
    "--agent", "PrReviewBrief",
    "--message", `Summarise PR #${pr.number}: ${pr.title}\n${pr.html_url}\n${(pr.body || "").slice(0, 1500)}`,
    "--json"
  ], (err, stdout) => {
    if (err) console.error(err);
    // parse stdout JSON and send summary text to Telegram via your helper
  });

  res.status(202).end();
});

app.listen(8787, "127.0.0.1");

Put Caddy/Nginx in front with HTTPS; keep Node on localhost. Firewall: only 443 public. Optionally add --deliver when the agent is already wired to Telegram.

6. GitHub webhook settings

  1. Repo → Settings → Webhooks → Add webhook
  2. Payload URL: https://hooks.example.com/github/webhook
  3. Content type: application/json
  4. Secret: same as GITHUB_WEBHOOK_SECRET
  5. Events: at least Pull requests

7. Example Telegram digest

PR #128 opened by alice — “Fix session expiry on mobile”
Intent: tighten token refresh when app resumes from background
Risk files: src/auth/session.ts, src/api/client.ts
Concerns: no test for resume-from-background; check logout race
Next: needs human on auth path; CI green first
https://github.com/org/repo/pull/128

8. Debugging notes from first runs

  • 401 from signature checks — raw body must be used for HMAC; parsing JSON before verify breaks the digest.
  • Timeouts on large diffs — truncate patch text; summarize file list first, fetch patch only for top risk files.
  • Noise — ignore labeled/edited until you want them; start with opened + synchronize.
  • Secret hygiene — webhook secret ≠ GitHub PAT; rotate if the receiver was briefly public.

Channel issues: channels troubleshooting. Gateway: gateway. Useful commands: openclaw gateway status, openclaw doctor, openclaw logs --follow.

9. Performance and cost

  • Classifier gate: skip AI if the PR is docs-only (path heuristics).
  • Batch: optional cron digest of open PRs instead of every push (cron).
  • Token budget: mini model for triage; escalate on security/auth paths.

10. Extensions

Last updated: 2026-07-14 · About this site