diff --git a/server/src/services/chat-channels.ts b/server/src/services/chat-channels.ts index 727a8f74f5a..fc554723f06 100644 --- a/server/src/services/chat-channels.ts +++ b/server/src/services/chat-channels.ts @@ -19,7 +19,7 @@ import { createInboundWakeNotices } from "./chat-channels/inbound/wakeup-notices import { createDiscordRuntimeReconciliation } from "./chat-channels/discord/runtime-reconciliation.js"; import { createPhotonRuntimeCallbacks } from "./chat-channels/imessage-photon/runtime-callbacks.js"; import { createChatReceiverOwnership } from "./chat-channels/runtime/ownership.js"; -import { createGitHubWebhookScope } from "./chat-channels/github/webhook-scope.js"; +import { createGitHubWebhookScope, githubWebhookCanRecoverRevokedInstallation } from "./chat-channels/github/webhook-scope.js"; import { createGitHubWebhookAuthentication, retryableGitHubWebhookResponse } from "./chat-channels/github/webhook-authentication.js"; import { createSlackWebhookCallbacks } from "./chat-channels/slack/webhook-callbacks.js"; import { createGitHubWebhookEvents } from "./chat-channels/github/webhook-events.js"; @@ -110,7 +110,7 @@ import { import { telegramLifecycleEventFromPayload, telegramIgnoresInboundMessage } from "./chat-channels/telegram/messages.js"; import { - microsoftTeamsLifecycleEventFromPayload, teamsOrphanFollowUp, teamsAttachmentReferenceText, + teamsWebhookLifecycleEventFromPayload, teamsOrphanFollowUp, teamsAttachmentReferenceText, } from "./chat-channels/microsoft-teams/messages.js"; import { githubSupplementalLifecycleFromRequest, @@ -2888,9 +2888,7 @@ export function chatChannelService(db: Db, options: ChatChannelServiceOptions) { ) .then((rows) => rows[0] ?? null); const recoveringRevokedGitHubInstallation = - endpoint?.status === "revoked" && - provider === "github" && - request.headers.get("x-github-event") === "installation"; + githubWebhookCanRecoverRevokedInstallation(endpoint?.status, provider, request); if ( !endpoint || endpoint.status === "archived" || @@ -2985,40 +2983,14 @@ export function chatChannelService(db: Db, options: ChatChannelServiceOptions) { if (slackCallbackInspection && response.ok) { await recordSlackWebhookCallback(endpoint, request, slackCallbackInspection, runtimeContext); } - if (githubInspection && response.ok) { - const lifecycle = await githubSupplementalLifecycleFromRequest(githubInspection); - if (lifecycle) { - await recordLifecycleDelivery( - { - endpointId: endpoint.id, - ...lifecycle, - }, - runtimeContext, - ); - } - } - if (provider === "telegram" && response.ok && lifecyclePayload) { - const lifecycle = telegramLifecycleEventFromPayload(lifecyclePayload); - if (lifecycle) { - await recordLifecycleDelivery( - { - endpointId: endpoint.id, - ...lifecycle, - }, - runtimeContext, - ); - } - } - if ( - provider === "microsoft-teams" && - response.ok && - lifecyclePayload && - endpointRuntime.acceptsProviderScope(lifecyclePayload) - ) { - const lifecycle = microsoftTeamsLifecycleEventFromPayload( - lifecyclePayload, - endpointRuntime, - ); + if (response.ok) { + const lifecycle = githubInspection + ? await githubSupplementalLifecycleFromRequest(githubInspection) + : provider === "telegram" && lifecyclePayload + ? telegramLifecycleEventFromPayload(lifecyclePayload) + : provider === "microsoft-teams" + ? teamsWebhookLifecycleEventFromPayload(lifecyclePayload, endpointRuntime) + : null; if (lifecycle) { await recordLifecycleDelivery( { diff --git a/server/src/services/chat-channels/github/webhook-scope.ts b/server/src/services/chat-channels/github/webhook-scope.ts index f50020e3dbf..906d9c59eef 100644 --- a/server/src/services/chat-channels/github/webhook-scope.ts +++ b/server/src/services/chat-channels/github/webhook-scope.ts @@ -1,4 +1,4 @@ -import type { LifecycleRuntimeFence } from "../runtime-types.js"; +import type { EndpointRow, LifecycleRuntimeFence } from "../runtime-types.js"; import type { ChatSdkProvider } from "../../chat-sdk-runtime.js"; export type GitHubIngressContext = { githubIngressActionId: string; githubIngressFence: LifecycleRuntimeFence; superseded?: boolean }; export function createGitHubWebhookScope(provider: ChatSdkProvider, internalContext: GitHubIngressContext | undefined, responseBudgetMs: number | undefined, GITHUB_WEBHOOK_RESPONSE_BUDGET_MS: number) { @@ -58,3 +58,12 @@ export function createGitHubWebhookScope(provider: ChatSdkProvider, internalCont return { replayingDurableGitHubIngress, ignoreSupersededIngress, matchesGitHubIngressFence, githubResponseDeadlineAt, completeGitHubPreflightBeforeDeadline }; } export type GitHubWebhookScope = ReturnType; + +export function githubWebhookCanRecoverRevokedInstallation( + status: EndpointRow["status"] | undefined, + provider: ChatSdkProvider, + request: Pick, +): boolean { + return status === "revoked" && provider === "github" && + request.headers.get("x-github-event") === "installation"; +} diff --git a/server/src/services/chat-channels/microsoft-teams/messages.ts b/server/src/services/chat-channels/microsoft-teams/messages.ts index 9c27c732e67..b7bb96de2a5 100644 --- a/server/src/services/chat-channels/microsoft-teams/messages.ts +++ b/server/src/services/chat-channels/microsoft-teams/messages.ts @@ -107,3 +107,11 @@ export function teamsOrphanFollowUp(threadId: string, messageId: string): boolea export function teamsAttachmentReferenceText(count: number, providerUrl: string | null): string { return `Shared ${count} Microsoft Teams file reference${count === 1 ? "" : "s"}.${providerUrl ? ` Open in Microsoft Teams: ${providerUrl}` : ""}`; } + +export function teamsWebhookLifecycleEventFromPayload( + payload: unknown, + endpointRuntime: Pick, +): MicrosoftTeamsLifecycleEvent | null { + if (!payload || !endpointRuntime.acceptsProviderScope(payload)) return null; + return microsoftTeamsLifecycleEventFromPayload(payload, endpointRuntime); +}