treewide: make less webhook-centric

Signed-off-by: NotAShelf <raf@notashelf.dev>
Change-Id: Ifab58fcb523549ca9cb83dc8467be51e6a6a6964
This commit is contained in:
raf 2026-02-01 14:38:58 +03:00
commit 374408834b
Signed by: NotAShelf
GPG key ID: 29D95B64378DB4BF
9 changed files with 479 additions and 39 deletions

View file

@ -1,11 +1,12 @@
import crypto from 'node:crypto';
import express from 'express';
import rateLimit from 'express-rate-limit';
import type { Config, WebhookEvent, AnalysisResult } from './types.js';
import type { Config, WebhookEvent } from './types.js';
import { shouldProcess } from './filters.js';
import { createEngine } from './engine/index.js';
import {
fetchPR,
fetchIssue,
formatComment,
hasExistingComment,
postComment,
@ -96,6 +97,21 @@ export function createApp(config: Config): express.Express {
return;
}
// Handle issue_comment with @troutbot mention - on-demand analysis
if (
eventType === 'issue_comment' &&
['created', 'edited'].includes(payload.action as string)
) {
const commentBody = (payload.comment as Record<string, unknown>).body as string;
if (commentBody && commentBody.includes('@troutbot')) {
const result = await handleOnDemandAnalysis(payload, config, engine);
res.json(result);
return;
}
res.json({ skipped: true, reason: 'Comment does not mention @troutbot' });
return;
}
if (eventType !== 'issues' && eventType !== 'pull_request') {
res.json({ skipped: true, reason: `Unhandled event: ${eventType}` });
return;
@ -241,6 +257,77 @@ async function handleCheckSuiteCompleted(
}
}
async function handleOnDemandAnalysis(
payload: Record<string, unknown>,
config: Config,
engine: ReturnType<typeof createEngine>
): Promise<Record<string, unknown>> {
const logger = getLogger();
const repo = payload.repository as Record<string, unknown>;
const owner = (repo.owner as Record<string, unknown>).login as string;
const repoName = repo.name as string;
const issue = payload.issue as Record<string, unknown>;
const issueNumber = issue.number as number;
const isPullRequest = issue.pull_request !== undefined;
logger.info(
`On-demand analysis triggered for ${owner}/${repoName}#${issueNumber} (${isPullRequest ? 'PR' : 'issue'})`
);
try {
let event: WebhookEvent;
if (isPullRequest) {
const prData = await fetchPR(owner, repoName, issueNumber);
if (!prData) {
logger.warn(`Could not fetch PR ${owner}/${repoName}#${issueNumber}`);
return { skipped: true, reason: 'Could not fetch PR data' };
}
event = {
action: 'on_demand',
type: 'pull_request',
number: issueNumber,
title: prData.title,
body: prData.body,
owner,
repo: repoName,
author: prData.author,
labels: prData.labels,
branch: prData.branch,
sha: prData.sha,
};
} else {
const issueData = await fetchIssue(owner, repoName, issueNumber);
if (!issueData) {
logger.warn(`Could not fetch issue ${owner}/${repoName}#${issueNumber}`);
return { skipped: true, reason: 'Could not fetch issue data' };
}
event = {
action: 'on_demand',
type: 'issue',
number: issueNumber,
title: issueData.title,
body: issueData.body,
owner,
repo: repoName,
author: issueData.author,
labels: issueData.labels,
};
}
return await analyzeAndComment(event, config, engine);
} catch (err) {
logger.error(
`Failed to process on-demand analysis for ${owner}/${repoName}#${issueNumber}`,
err
);
return { error: 'Internal server error' };
}
}
function parseEvent(eventType: string, payload: Record<string, unknown>): WebhookEvent | null {
try {
if (eventType === 'issues') {