feat(bot): add self-hosted scheduled dispatch support

Co-authored-by: claw <stanislavkalishin+claw@gmail.com>
This commit is contained in:
2026-03-30 15:27:15 +02:00
parent 94c1f48794
commit 575a68b3bb
13 changed files with 331 additions and 40 deletions

View File

@@ -39,6 +39,7 @@ import { registerHouseholdSetupCommands } from './household-setup'
import { HouseholdContextCache } from './household-context-cache'
import { createAwsScheduledDispatchScheduler } from './aws-scheduled-dispatch-scheduler'
import { createGcpScheduledDispatchScheduler } from './gcp-scheduled-dispatch-scheduler'
import { createSelfHostedScheduledDispatchScheduler } from './self-hosted-scheduled-dispatch-scheduler'
import { createMiniAppAuthHandler, createMiniAppJoinHandler } from './miniapp-auth'
import {
createMiniAppApproveMemberHandler,
@@ -139,21 +140,24 @@ export async function createBotRuntimeApp(): Promise<BotRuntimeApp> {
? createDbScheduledDispatchRepository(runtime.databaseUrl)
: null
const scheduledDispatchScheduler =
runtime.scheduledDispatch && runtime.schedulerSharedSecret
runtime.scheduledDispatch &&
(runtime.scheduledDispatch.provider === 'self-hosted' || runtime.schedulerSharedSecret)
? runtime.scheduledDispatch.provider === 'gcp-cloud-tasks'
? createGcpScheduledDispatchScheduler({
projectId: runtime.scheduledDispatch.projectId,
location: runtime.scheduledDispatch.location,
queue: runtime.scheduledDispatch.queue,
publicBaseUrl: runtime.scheduledDispatch.publicBaseUrl,
sharedSecret: runtime.schedulerSharedSecret
})
: createAwsScheduledDispatchScheduler({
region: runtime.scheduledDispatch.region,
targetLambdaArn: runtime.scheduledDispatch.targetLambdaArn,
roleArn: runtime.scheduledDispatch.roleArn,
groupName: runtime.scheduledDispatch.groupName
sharedSecret: runtime.schedulerSharedSecret!
})
: runtime.scheduledDispatch.provider === 'aws-eventbridge'
? createAwsScheduledDispatchScheduler({
region: runtime.scheduledDispatch.region,
targetLambdaArn: runtime.scheduledDispatch.targetLambdaArn,
roleArn: runtime.scheduledDispatch.roleArn,
groupName: runtime.scheduledDispatch.groupName
})
: createSelfHostedScheduledDispatchScheduler()
: null
const scheduledDispatchService =
scheduledDispatchRepositoryClient &&
@@ -514,7 +518,7 @@ export async function createBotRuntimeApp(): Promise<BotRuntimeApp> {
event: 'runtime.feature_disabled',
feature: 'scheduled-dispatch'
},
'Scheduled dispatch is disabled. Configure DATABASE_URL, SCHEDULED_DISPATCH_PROVIDER, and scheduler auth to enable reminder delivery.'
'Scheduled dispatch is disabled. Configure DATABASE_URL and SCHEDULED_DISPATCH_PROVIDER to enable reminder delivery.'
)
}
@@ -933,6 +937,12 @@ export async function createBotRuntimeApp(): Promise<BotRuntimeApp> {
oidcAllowedEmails: runtime.schedulerOidcAllowedEmails
}).authorize,
handler: async (request, jobPath) => {
if (jobPath === 'dispatch-due') {
return scheduledDispatchHandler
? scheduledDispatchHandler.handleDueDispatches(request)
: new Response('Not Found', { status: 404 })
}
if (jobPath.startsWith('dispatch/')) {
return scheduledDispatchHandler
? scheduledDispatchHandler.handle(request, jobPath.slice('dispatch/'.length))
@@ -949,6 +959,12 @@ export async function createBotRuntimeApp(): Promise<BotRuntimeApp> {
oidcAllowedEmails: runtime.schedulerOidcAllowedEmails
}).authorize,
handler: async (request, jobPath) => {
if (jobPath === 'dispatch-due') {
return scheduledDispatchHandler
? scheduledDispatchHandler.handleDueDispatches(request)
: new Response('Not Found', { status: 404 })
}
if (jobPath.startsWith('dispatch/')) {
return scheduledDispatchHandler
? scheduledDispatchHandler.handle(request, jobPath.slice('dispatch/'.length))