diff --git a/bun.lockb b/bun.lockb index c5df9fd..224c994 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/src/handlers/app.handler.ts b/src/handlers/app.handler.ts index 3b179df..c05db84 100644 --- a/src/handlers/app.handler.ts +++ b/src/handlers/app.handler.ts @@ -2,7 +2,14 @@ import { Context, Probot } from 'probot'; import { envs } from '~common/consts'; import { Config } from '~common/types'; import { UserErrorListener } from '~events'; -import { ChatHandler, DiagramHandler, ReviewHandler, SummaryHandler, WalkthroughHandler } from '~handlers'; +import { + ChatCommandHandler, + ChatHandler, + DiagramHandler, + ReviewHandler, + SummaryHandler, + WalkthroughHandler +} from '~handlers'; import { systemLogger } from '~loggers'; import { AiService, AppService, CacheService, ConfigService, ErrorService, QueueService } from '~services'; import { ContributorHandler } from './contributor.handler'; @@ -37,6 +44,7 @@ export class AppHandler { pushHandler: PushHandler; diagramHandler: DiagramHandler; installationHandler: installationHandler; + chatCommandHandler: ChatCommandHandler; }; constructor() { @@ -78,6 +86,11 @@ export class AppHandler { this.services.queueService, this.services.cacheService ), + chatCommandHandler: new ChatCommandHandler( + this.services.appService, + this.services.queueService, + this.services.cacheService + ), contributorHandler: new ContributorHandler(this.services.cacheService, this.services.queueService), pushHandler: new PushHandler(this.services.cacheService, this.services.configService), installationHandler: new installationHandler(this.services.cacheService) @@ -134,6 +147,17 @@ export class AppHandler { }); } + private async handleCommandEvents(context: Context<'pull_request_review_comment' | 'pull_request'>): Promise { + await this.safeEventHandler(context, async (apiKey, config) => { + await this.handlers.contributorHandler.safeCacheContributors(context, apiKey); + const chatX = await this.handlers.chatCommandHandler.handle(context, apiKey, config); + + if (chatX.reviewTriggered) { + await this.handlers.reviewHandler.handle(context, apiKey, config); + } + }); + } + private async handleMemberEvents(context: Context<'member'>): Promise { await this.safeEventHandler(context, (apiKey) => this.handlers.contributorHandler.handle(context, apiKey)); } @@ -167,6 +191,14 @@ export class AppHandler { this.handleCommentEvents.bind(this) ); + app.on( + [ + 'pull_request_review_comment.created', + 'pull_request' + ], + this.handleCommandEvents.bind(this) + ); + app.on( [ 'member.added', diff --git a/src/handlers/chat-commands.handler.ts b/src/handlers/chat-commands.handler.ts new file mode 100644 index 0000000..f5a9b0d --- /dev/null +++ b/src/handlers/chat-commands.handler.ts @@ -0,0 +1,86 @@ +import { WebhookEventName } from '@octokit/webhooks-types'; +import { Context } from 'probot'; +import { Config } from '~common/types'; +import { SystemError } from '~errors'; +import { isInlineComment } from '~helpers'; +import { AppService, CacheService, GitHubService, QueueService } from '~services'; +import { ChatSkipConditions } from '~utils'; + +type CommentEvent = + | 'issue_comment' + | 'discussion_comment' + | 'pull_request_review_comment' + | 'discussion' + | 'issues' + | 'pull_request'; + +interface Handler { + handle( + context: Context, + apiKey: string, + config: Config + ): Promise<{ + reviewTriggered: boolean; + }>; +} + +export class ChatCommandHandler implements Handler { + constructor( + private readonly appService: AppService, + private readonly queueService: QueueService, + private readonly cacheService: CacheService + ) {} + + async handle( + context: Context, + apiKey: string, + config: Config + ): Promise<{ + reviewTriggered: boolean; + }> { + const shouldSkip = await new ChatSkipConditions( + context, + config, + this.appService, + this.cacheService, + this.queueService, + apiKey + ).shouldSkip(); + + if (shouldSkip) { + return { + reviewTriggered: false + }; + } + const contextBody = await this.getContextBody(context); + + if (contextBody?.includes('dr-github-pro review')) { + return { reviewTriggered: true }; + } + + return { + reviewTriggered: false + }; + } + + private async getContextBody(context: Context): Promise { + return this.queueService.schedule(async () => { + if (isInlineComment(context.payload) && context.payload.comment.in_reply_to_id) { + const reviewComment = await GitHubService.getReviewComment(context, context.payload.comment.in_reply_to_id); + + return reviewComment.data.body; + } + + if ('comment' in context.payload) { + return context.payload.comment.body; + } else if ('discussion' in context.payload) { + return context.payload.discussion.body; + } else { + if ('issue' in context.payload) { + return context.payload.issue.body; + } + throw new SystemError('Unhandled payload type'); + } + }); + } +} diff --git a/src/handlers/index.ts b/src/handlers/index.ts index e61bebd..6ca1549 100644 --- a/src/handlers/index.ts +++ b/src/handlers/index.ts @@ -1,4 +1,5 @@ export * from './app.handler'; +export * from './chat-commands.handler'; export * from './chat.handler'; export * from './contributor.handler'; export * from './diagram.handler';